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,
],
],
];