Skip to content

Commit

Permalink
[FEATURE] Improve file processing
Browse files Browse the repository at this point in the history
- new simplified output (off by default)
- new options to process files (custom autogenerate feature), filter & alias properties for much cleaned up output. Useful with large sites with a lot of images on page. See options Documentation/Developer/Images.rst
- code refactoring
- allows to process more image formats

Resolves: #247, #617
  • Loading branch information
twoldanski committed May 9, 2024
1 parent 3f75d3b commit f749fc5
Show file tree
Hide file tree
Showing 10 changed files with 689 additions and 210 deletions.
81 changes: 9 additions & 72 deletions Classes/DataProcessing/FilesProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace FriendsOfTYPO3\Headless\DataProcessing;

use FriendsOfTYPO3\Headless\Utility\File\ProcessingConfiguration;
use FriendsOfTYPO3\Headless\Utility\FileUtility;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand Down Expand Up @@ -174,89 +175,25 @@ protected function fetchData(): array
protected function processFiles(array $properties = []): ?array
{
$data = [];
$cropVariant = $this->processorConfiguration['processingConfiguration.']['cropVariant'] ?? 'default';

foreach ($this->fileObjects as $key => $fileObject) {
if (isset($this->processorConfiguration['processingConfiguration.']['autogenerate.'])) {
$file = $this->getFileUtility()->processFile(
$fileObject,
$properties,
$cropVariant,
(int)($this->processorConfiguration['processingConfiguration.']['delayProcessing'] ?? 0) === 1
);

$targetWidth = (int)($properties['width'] ?: $file['properties']['dimensions']['width']);
$targetHeight = (int)($properties['height'] ?: $file['properties']['dimensions']['height']);

if (isset($this->processorConfiguration['processingConfiguration.']['autogenerate.']['retina2x']) &&
(int)$this->processorConfiguration['processingConfiguration.']['autogenerate.']['retina2x'] === 1 &&
($targetWidth || $targetHeight)) {
$file['urlRetina'] = $this->getFileUtility()->processFile(
$fileObject,
array_merge(
$properties,
[
'width' => $targetWidth * FileUtility::RETINA_RATIO,
'height' => $targetHeight * FileUtility::RETINA_RATIO,
]
),
$cropVariant,
(int)($this->processorConfiguration['processingConfiguration.']['delayProcessing'] ?? 0) === 1
)['publicUrl'];
}

if (isset($this->processorConfiguration['processingConfiguration.']['autogenerate.']['lqip']) &&
(int)$this->processorConfiguration['processingConfiguration.']['autogenerate.']['lqip'] === 1 &&
($targetWidth || $targetHeight)) {
$file['urlLqip'] = $this->getFileUtility()->processFile(
$fileObject,
array_merge(
$properties,
[
'width' => $targetWidth * FileUtility::LQIP_RATIO,
'height' => $targetHeight * FileUtility::LQIP_RATIO,
]
),
$cropVariant,
(int)($this->processorConfiguration['processingConfiguration.']['delayProcessing'] ?? 0) === 1
)['publicUrl'];
}

$data[] = $file;
} else {
$data[$key] = $this->getFileUtility()->processFile(
$fileObject,
$properties,
$cropVariant,
(int)($this->processorConfiguration['processingConfiguration.']['delayProcessing'] ?? 0) === 1
);
$processingConfiguration = ProcessingConfiguration::fromOptions($properties);

$crop = $fileObject->getProperty('crop');

if ($crop !== null) {
$cropVariants = json_decode($fileObject->getProperty('crop'), true);
foreach ($this->fileObjects as $key => $fileObject) {
$data[$key] = $this->getFileUtility()->process(
$fileObject,
$processingConfiguration,
);

if (is_array($cropVariants) && count($cropVariants) > 1) {
foreach (array_keys($cropVariants) as $cropVariantName) {
$file = $this->getFileUtility()->processFile($fileObject, $properties, $cropVariantName);
$data[$key]['cropVariants'][$cropVariantName] = $file;
}
}
}
}
$data[$key] = $this->getFileUtility()->processCropVariants($fileObject, $processingConfiguration, $data[$key]);
}

if (isset($this->processorConfiguration['processingConfiguration.']['returnFlattenObject']) &&
(int)$this->processorConfiguration['processingConfiguration.']['returnFlattenObject'] === 1) {
if ($processingConfiguration->flattenObject) {
return $data[0] ?? null;
}

return $data;
}

/**
* @return FileUtility
*/
protected function getFileUtility(): FileUtility
{
return GeneralUtility::makeInstance(FileUtility::class);
Expand Down
60 changes: 23 additions & 37 deletions Classes/DataProcessing/GalleryProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace FriendsOfTYPO3\Headless\DataProcessing;

use FriendsOfTYPO3\Headless\Utility\File\ProcessingConfiguration;
use FriendsOfTYPO3\Headless\Utility\FileUtility;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\FileInterface;
Expand All @@ -36,6 +37,8 @@ class GalleryProcessor extends \TYPO3\CMS\Frontend\DataProcessing\GalleryProcess
*/
protected $fileObjects = [];

protected ProcessingConfiguration $processorConfigurationObject;

/**
* @inheritDoc
*/
Expand All @@ -45,6 +48,8 @@ public function process(
array $processorConfiguration,
array $processedData
) {
$this->processorConfigurationObject = ProcessingConfiguration::fromOptions($processorConfiguration);

$processedData = parent::process(
$cObj,
$contentObjectConfiguration,
Expand Down Expand Up @@ -159,11 +164,20 @@ protected function calculateMediaWidthsAndHeights()
*/
private function getCroppedDimensionalPropertyFromProcessedFile(array $processedFile, string $property): int
{
if (empty($processedFile['properties']['crop'])) {
return (int)$processedFile['properties']['dimensions'][$property];
if ($this->processorConfigurationObject->legacyReturn) {
if (empty($processedFile['properties']['crop'])) {
return (int)($this->processorConfigurationObject->flattenProperties ? ($processedFile['properties'][$property] ?? 0) : ($processedFile['properties']['dimensions'][$property] ?? 0));
}

$croppingConfiguration = $processedFile['properties']['crop'];
} else {
if (empty($processedFile['crop'])) {
return (int)($this->processorConfigurationObject->flattenProperties ? ($processedFile[$property] ?? 0) : ($processedFile['dimensions'][$property] ?? 0));
}

$croppingConfiguration = $processedFile['crop'];
}

$croppingConfiguration = $processedFile['properties']['crop'];
$cropVariantCollection = CropVariantCollection::create((string)$croppingConfiguration);

return (int)$cropVariantCollection->getCropArea($this->cropVariant)
Expand All @@ -184,43 +198,15 @@ protected function prepareGalleryData()
$fileObj = $this->fileObjects[$fileKey] ?? null;

if ($fileObj) {
$fileExtension = $this->processorConfiguration['fileExtension'] ?? null;

if ($fileObj['properties']['type'] === 'image') {
$image = $this->getImageService()->getImage((string)$fileObj['properties']['fileReferenceUid'], null, true);
$fileObj = $this->getFileUtility()->processFile(
if ((($fileObj['properties']['type'] ?? '') === 'image' || ($fileObj['type'] ?? '') === 'image')) {
$src = $this->processorConfigurationObject->legacyReturn ? $fileObj['properties']['fileReferenceUid'] : $fileObj['fileReferenceUid'];
$image = $this->getImageService()->getImage((string)$src, null, true);
$fileObj = $this->getFileUtility()->process(
$image,
array_merge(
['fileExtension' => $fileExtension],
$this->mediaDimensions[$fileKey] ?? []
)
$this->processorConfigurationObject->withOptions($this->mediaDimensions[$fileKey] ?? [])
);

if (isset($this->processorConfiguration['autogenerate.']['retina2x'],
$fileObj['properties']['dimensions']['width']) &&
(int)$this->processorConfiguration['autogenerate.']['retina2x'] === 1) {
$fileObj['urlRetina'] = $this->getFileUtility()->processFile(
$image,
[
'fileExtension' => $fileExtension,
'width' => $fileObj['properties']['dimensions']['width'] * FileUtility::RETINA_RATIO,
'height' => $fileObj['properties']['dimensions']['height'] * FileUtility::RETINA_RATIO,
]
)['publicUrl'];
}

if (isset($this->processorConfiguration['autogenerate.']['lqip'],
$fileObj['properties']['dimensions']['width']) &&
(int)$this->processorConfiguration['autogenerate.']['lqip'] === 1) {
$fileObj['urlLqip'] = $this->getFileUtility()->processFile(
$image,
[
'fileExtension' => $fileExtension,
'width' => $fileObj['properties']['dimensions']['width'] * FileUtility::LQIP_RATIO,
'height' => $fileObj['properties']['dimensions']['height'] * FileUtility::LQIP_RATIO,
]
)['publicUrl'];
}
$fileObj = $this->getFileUtility()->processCropVariants($image, $this->processorConfigurationObject, $fileObj);
}

$this->galleryData['rows'][$row]['columns'][$column] = $fileObj;
Expand Down
19 changes: 11 additions & 8 deletions Classes/Event/EnrichFileDataEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,19 @@

namespace FriendsOfTYPO3\Headless\Event;

use FriendsOfTYPO3\Headless\Utility\File\ProcessingConfiguration;
use TYPO3\CMS\Core\Resource\FileInterface;

final class EnrichFileDataEvent
{
private FileInterface $original;
private FileInterface $processed;
private array $properties;

public function __construct(
FileInterface $originalFileReference,
FileInterface $processedFileReference,
private readonly FileInterface $originalFileReference,
private readonly FileInterface $processedFileReference,
private readonly ProcessingConfiguration $processingConfiguration,
array $properties = []
) {
$this->original = $originalFileReference;
$this->processed = $processedFileReference;
$this->properties = $properties;
}

Expand All @@ -41,11 +39,16 @@ public function setProperties(array $properties): void

public function getProcessed(): FileInterface
{
return $this->processed;
return $this->processedFileReference;
}

public function getOriginal(): FileInterface
{
return $this->original;
return $this->originalFileReference;
}

public function getProcessingConfiguration(): ProcessingConfiguration
{
return $this->processingConfiguration;
}
}
95 changes: 95 additions & 0 deletions Classes/Utility/File/ProcessingConfiguration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/*
* This file is part of the "headless" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/

declare(strict_types=1);

namespace FriendsOfTYPO3\Headless\Utility\File;

use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* @codeCoverageIgnore
*/
class ProcessingConfiguration
{
private const RETINA_RATIO = 2;
private const LQIP_RATIO = 0.1;

public static function fromOptions(array $options): static
{
return new static(
(int)($options['width'] ?? 0),
(int)($options['height'] ?? 0),
(int)($options['minWidth'] ?? 0),
(int)($options['minHeight'] ?? 0),
(int)($options['maxWidth'] ?? 0),
(int)($options['maxHeight'] ?? 0),
($options['fileExtension'] ?? null),
$options['cropVariant'] ?? 'default',
(int)($options['conditionalCropVariant'] ?? 0) > 0,
(int)($options['legacyReturn'] ?? 1) > 0,
(int)($options['returnFlattenObject'] ?? 0) > 0,
(int)($options['delayProcessing'] ?? 0) > 0,
(int)($options['cacheBusting'] ?? 0) > 0,
(int)($options['linkResult'] ?? 0) > 0,
(int)($options['properties.']['flatten'] ?? 0) > 0,
((int)($options['properties.']['byType'] ?? 0)) > 0,
GeneralUtility::trimExplode(',', $options['properties.']['includeOnly'] ?? '', true),
self::handleLegacyOptions($options['autogenerate.'] ?? []),
$options
);
}

public static function legacy(bool $delayProcessing): static
{
return new static(legacyReturn: true, delayProcessing: $delayProcessing);
}

private function __construct(
public readonly int $width = 0,
public readonly int $height = 0,
public readonly int $minWidth = 0,
public readonly int $minHeight = 0,
public readonly int $maxWidth = 0,
public readonly int $maxHeight = 0,
public readonly ?string $fileExtension = null,
public readonly string $cropVariant = 'default',
public readonly bool $conditionalCropVariant = false,
public readonly bool $legacyReturn = true,
public readonly bool $flattenObject = false,
public readonly bool $delayProcessing = false,
public readonly bool $cacheBusting = false,
public readonly bool $linkResult = false,
public readonly bool $flattenProperties = false,
public readonly bool $propertiesByType = false,
public readonly array $includeProperties = [],
public readonly array $autogenerate = [],
public readonly array $rawOptions = [],
) {}

private static function handleLegacyOptions(array $configuration): array
{
if ((int)($configuration['retina2x'] ?? 0)) {
$configuration['urlRetina'] = ['factor' => self::RETINA_RATIO];
unset($configuration['retina2x']);
}

if ((int)($configuration['lqip'] ?? 0)) {
$configuration['urlLqip'] = ['factor' => self::LQIP_RATIO];
unset($configuration['lqip']);
}

return $configuration;
}

public function withOptions(array $options): static
{
return self::fromOptions(array_merge($this->rawOptions, $options));
}
}
Loading

0 comments on commit f749fc5

Please sign in to comment.