diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3d110a576e..47d22c7d31 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -192,7 +192,7 @@ jobs: image: redis ports: - 6379:6379 - options: + options: --memory=60m solr: image: ghcr.io/ibexa/core/solr @@ -210,7 +210,7 @@ jobs: - '7.4' - '8.0' - '8.1' - steps: + steps: - uses: actions/checkout@v2 with: fetch-depth: 0 @@ -220,10 +220,10 @@ jobs: with: php-version: ${{ matrix.php }} coverage: none - + - name: Add solr dependency run: | - VERSION=$(jq -r '.extra | ."branch-alias" | ."dev-main"' < composer.json) + VERSION="dev-ibx-6620-added-image-criterion-visitors-for-ci as 4.6.x-dev" composer require --no-update "ibexa/solr:$VERSION" - uses: "ramsey/composer-install@v1" diff --git a/phpunit-integration-legacy-solr.xml b/phpunit-integration-legacy-solr.xml index 3dce726268..8c5db18b42 100644 --- a/phpunit-integration-legacy-solr.xml +++ b/phpunit-integration-legacy-solr.xml @@ -18,7 +18,9 @@ - + + + diff --git a/phpunit-integration-legacy.xml b/phpunit-integration-legacy.xml index 6b1da1c84c..cb4a636435 100644 --- a/phpunit-integration-legacy.xml +++ b/phpunit-integration-legacy.xml @@ -18,6 +18,7 @@ + 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..5dc965b06f --- /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 Criterion\Image\AbstractImageCompositeCriterion +{ + public const IMAGE_SEARCH_CRITERIA = [ + 'mimeTypes', + 'size', + 'width', + 'height', + 'orientation', + ]; + + protected function getSupportedCriteria(): array + { + return self::IMAGE_SEARCH_CRITERIA; + } + + /** + * @phpstan-param ImageCriteria $data + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function buildCriteria( + string $fieldDefIdentifier, + array $data + ): array { + $criteria = []; + + if (isset($data['mimeTypes'])) { + $criteria[] = new MimeType( + $fieldDefIdentifier, + $data['mimeTypes'] + ); + } + + if (isset($data['size'])) { + $size = $data['size']; + $criteria[] = new FileSize( + $fieldDefIdentifier, + $this->getMinValue($size), + $this->getMaxValue($size), + ); + } + + if (isset($data['width'])) { + $width = $data['width']; + $criteria[] = new Width( + $fieldDefIdentifier, + $this->getMinValue($width), + $this->getMaxValue($width) + ); + } + + if (isset($data['height'])) { + $height = $data['height']; + $criteria[] = new Height( + $fieldDefIdentifier, + $this->getMinValue($height), + $this->getMaxValue($height) + ); + } + + if (isset($data['orientation'])) { + $criteria[] = new Orientation( + $fieldDefIdentifier, + $data['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..f3b43d1e99 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageCompositeCriterion.php @@ -0,0 +1,108 @@ +validate($data, $this->getSupportedCriteria()); + + $criteria = new Criterion\LogicalAnd( + $this->buildCriteria($fieldDefIdentifier, $data) + ); + + parent::__construct($criteria); + } + + /** + * @phpstan-param TImageCriteria $data + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + */ + abstract protected function buildCriteria(string $fieldDefIdentifier, array $data): array; + + /** + * @return array + */ + abstract protected function getSupportedCriteria(): array; + + /** + * @phpstan-param TImageCriteria $data + * + * @param array $supportedCriteria + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function validate( + array $data, + array $supportedCriteria + ): void { + if (empty($data)) { + throw new InvalidArgumentException( + '$data', + sprintf( + 'At least one of the supported criteria should be passed: "%s"', + implode(', ', $supportedCriteria) + ) + ); + } + + $notSupportedCriteria = array_diff( + array_keys($data), + $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?: int|null} $data + */ + protected function getMinValue(array $data): int + { + return $data['min'] ?? 0; + } + + /** + * @param array{max?: int|null} $data + */ + protected function getMaxValue(array $data): ?int + { + 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..31bc7c6c3d --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageRangeCriterion.php @@ -0,0 +1,93 @@ +validate($minValue, $maxValue); + + $value[] = $minValue; + $operator = Operator::GTE; + + if ($maxValue >= 1) { + $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 + ), + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function validate( + int $minValue, + ?int $maxValue + ): void { + if ($minValue < 0) { + throw new InvalidArgumentException( + '$minValue', + 'Value should be grater or equal 0' + ); + } + + if ( + null !== $maxValue + && $maxValue < 1 + ) { + throw new InvalidArgumentException( + '$maxValue', + 'Value should be grater or equal 1' + ); + } + + if ( + null !== $maxValue + && $minValue > $maxValue + ) { + throw new InvalidArgumentException( + '$minValue', + 'Value should be grater 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..d8cc57cf0c --- /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 $data + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function buildCriteria( + string $fieldDefIdentifier, + array $data + ): array { + $criteria = []; + + if (isset($data['width'])) { + $width = $data['width']; + $criteria[] = new Width( + $fieldDefIdentifier, + $this->getMinValue($width), + $this->getMaxValue($width) + ); + } + + if (isset($data['height'])) { + $height = $data['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..63fc9f64d9 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/FileSize.php @@ -0,0 +1,35 @@ + 0) { + $minFileSize *= 1024 * 1024; + } + + if ($maxFileSize > 0) { + $maxFileSize *= 1024 * 1024; + } + + parent::__construct( + $fieldDefIdentifier, + $minFileSize, + $maxFileSize + ); + } +} 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 @@ +value->data['width'] ?? null; + $height = $field->value->data['height'] ?? null; + return [ new Search\Field( 'filename', @@ -39,6 +42,21 @@ public function getIndexData(Field $field, FieldDefinition $fieldDefinition) $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() + ), ]; } @@ -49,6 +67,9 @@ public function getIndexDefinition() '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(), ]; } @@ -79,6 +100,23 @@ 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 index 76bf759125..ef84995998 100644 --- a/src/lib/FieldType/Image/Type.php +++ b/src/lib/FieldType/Image/Type.php @@ -323,6 +323,7 @@ public function toHash(SPIValue $value) 'width' => $value->width, 'height' => $value->height, 'additionalData' => $value->additionalData, + 'mime' => $value->mime, ]; } @@ -387,6 +388,7 @@ public function fromPersistenceValue(FieldValue $fieldValue) ? $fieldValue->data['height'] : null), 'additionalData' => $fieldValue->data['additionalData'] ?? [], + 'mime' => $fieldValue->data['mime'] ?? null, ] ); @@ -409,6 +411,11 @@ public static function getTranslationMessages(): array 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 index 4ca142d03b..f8cf5645df 100644 --- a/src/lib/FieldType/Image/Value.php +++ b/src/lib/FieldType/Image/Value.php @@ -93,6 +93,8 @@ class Value extends BaseValue /** @var string[] */ public $additionalData = []; + public ?string $mime = null; + /** * Construct a new Value object. */ diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php index 80989af520..0b06c09c40 100644 --- a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php @@ -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 // diff --git a/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php b/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php index 8fa2e3e74e..ff9555acc0 100644 --- a/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php +++ b/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php @@ -9,6 +9,7 @@ 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\Value as ImageValue; /** @@ -341,6 +342,7 @@ public function provideToHashData() 'width' => null, 'height' => null, 'additionalData' => [], + 'mime' => null, ], ], [ @@ -354,6 +356,7 @@ public function provideToHashData() 'uri' => "/$path", 'width' => 123, 'height' => 456, + 'mime' => 'image/png', ] ), [ @@ -368,6 +371,7 @@ public function provideToHashData() 'width' => 123, 'height' => 456, 'additionalData' => [], + 'mime' => 'image/png', ], ], ]; @@ -573,6 +577,15 @@ public function testUpdateImageAltTextOnly(): void ); } + protected function checkSearchEngineSupport(): void + { + if ($this->getSetupFactory() instanceof Legacy) { + $this->markTestSkipped( + "'ezimage' field type is not searchable with Legacy Search Engine" + ); + } + } + protected function getValidSearchValueOne() { return new ImageValue( diff --git a/tests/integration/Core/Repository/SearchServiceImageTest.php b/tests/integration/Core/Repository/SearchServiceImageTest.php new file mode 100644 index 0000000000..b0586a14ab --- /dev/null +++ b/tests/integration/Core/Repository/SearchServiceImageTest.php @@ -0,0 +1,383 @@ +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 + */ + 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 + */ + 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 $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 $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 + ); + } + + 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 + ); + } +} 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/tests/integration/Core/Repository/_fixtures/image/portrait.jpg b/tests/integration/Core/Repository/_fixtures/image/portrait.jpg new file mode 100644 index 0000000000..0ddd840923 Binary files /dev/null and b/tests/integration/Core/Repository/_fixtures/image/portrait.jpg differ diff --git a/tests/integration/Core/Repository/_fixtures/image/square.png b/tests/integration/Core/Repository/_fixtures/image/square.png new file mode 100644 index 0000000000..159ab778de Binary files /dev/null and b/tests/integration/Core/Repository/_fixtures/image/square.png differ 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 @@ +get('ibexa.spi.search'); + if ( + class_exists(SolrHandler::class) + && $handler instanceof SolrHandler + ) { + $handler->commit(); + } + } +} diff --git a/tests/lib/FieldType/ImageTest.php b/tests/lib/FieldType/ImageTest.php index f89f425011..126639d1e1 100644 --- a/tests/lib/FieldType/ImageTest.php +++ b/tests/lib/FieldType/ImageTest.php @@ -336,6 +336,7 @@ public function provideInputForToHash() 'uri' => 'http://' . $this->getImageInputPath(), 'width' => 123, 'height' => 456, + 'mime' => 'image/jpeg', ] ), [ @@ -350,6 +351,7 @@ public function provideInputForToHash() 'width' => 123, 'height' => 456, 'additionalData' => [], + 'mime' => 'image/jpeg', ], ], // BC with 5.0 (EZP-20948). Path can be used as input instead of $inputUri. @@ -362,6 +364,7 @@ public function provideInputForToHash() 'alternativeText' => 'This is so Sindelfingen!', 'imageId' => '123-12345', 'uri' => 'http://' . $this->getImageInputPath(), + 'mime' => null, ] ), [ @@ -376,6 +379,7 @@ public function provideInputForToHash() 'width' => null, 'height' => null, 'additionalData' => [], + 'mime' => null, ], ], ];