Skip to content

Commit

Permalink
[FEATURE] Implement MIME type filtering for files in Helper class (#1338
Browse files Browse the repository at this point in the history
)
  • Loading branch information
fschoelzel authored and sebastian-meyer committed Oct 21, 2024
1 parent cb35a9e commit 881e450
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 91 deletions.
50 changes: 48 additions & 2 deletions Classes/Common/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Resource\MimeTypeCollection;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
Expand Down Expand Up @@ -622,7 +623,7 @@ public static function processDatabaseAsAdmin(array $data = [], array $cmd = [],
* Fetches and renders all available flash messages from the queue.
*
* @access public
*
*
* @static
*
* @param string $queue The queue's unique identifier
Expand Down Expand Up @@ -927,7 +928,7 @@ private static function getEncryptionKey(): ?string
* @access private
*
* @static
*
*
* @param string $path
*
* @return mixed
Expand All @@ -942,4 +943,49 @@ private static function getLocalConfigurationByPath(string $path)

return ArrayUtility::getValueByPath($GLOBALS['TYPO3_CONF_VARS'], $path);
}

/**
* Filters a file based on its mimetype categories.
*
* This method checks if the provided file array contains a specified mimetype key and
* verifies if the mimetype belongs to any of the specified categories or matches any of the additional custom mimetypes.
*
* @param mixed $file The file array to filter
* @param array $categories The MIME type categories to filter by (e.g., ['audio'], ['video'] or ['image', 'application'])
* @param array $dlfMimeTypes The custom DLF mimetype keys IIIF, IIP or ZOOMIFY to check against (default is an empty array)
* @param string $mimeTypeKey The key used to access the mimetype in the file array (default is 'mimetype')
*
* @return bool True if the file mimetype belongs to any of the specified categories or matches any custom mimetypes, false otherwise
*/
public static function filterFilesByMimeType($file, array $categories, array $dlfMimeTypes = [], string $mimeTypeKey = 'mimetype'): bool
{
// Retrieves MIME types from the TYPO3 Core MimeTypeCollection
$mimeTypeCollection = GeneralUtility::makeInstance(MimeTypeCollection::class);
$mimeTypes = array_filter(
$mimeTypeCollection->getMimeTypes(),
function ($mimeType) use ($categories) {
foreach ($categories as $category) {
if (strpos($mimeType, $category . '/') === 0) {
return true;
}
}
return false;
}
);

// Custom dlf MIME types
$dlfMimeTypeArray = [
'IIIF' => 'application/vnd.kitodo.iiif',
'IIP' => 'application/vnd.netfpx',
'ZOOMIFY' => 'application/vnd.kitodo.zoomify'
];

// Filter custom MIME types based on provided keys
$filteredDlfMimeTypes = array_intersect_key($dlfMimeTypeArray, array_flip($dlfMimeTypes));

if (is_array($file) && isset($file[$mimeTypeKey])) {
return in_array($file[$mimeTypeKey], $mimeTypes) || in_array($file[$mimeTypeKey], $filteredDlfMimeTypes);
}
return false;
}
}
87 changes: 49 additions & 38 deletions Classes/Controller/PageViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Kitodo\Dlf\Common\AbstractDocument;
use Kitodo\Dlf\Common\DocumentAnnotation;
use Kitodo\Dlf\Common\Helper;
use Kitodo\Dlf\Common\IiifManifest;
use Kitodo\Dlf\Common\MetsDocument;
use Kitodo\Dlf\Domain\Model\Document;
Expand Down Expand Up @@ -617,7 +618,6 @@ protected function getAnnotationContainers(int $page): array
* @access protected
*
* @param int $page Page number
*
* @param ?MetsDocument $specificDoc
*
* @return array URL and MIME type of image file
Expand All @@ -627,50 +627,61 @@ protected function getImage(int $page, MetsDocument $specificDoc = null): array
$image = [];
// Get @USE value of METS fileGrp.
$fileGrpsImages = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpImages']);
while ($fileGrpImages = array_pop($fileGrpsImages)) {
if ($specificDoc) {
// Get image link.
$physicalStructureInfo = $specificDoc->physicalStructureInfo[$specificDoc->physicalStructure[$page]];
$files = $physicalStructureInfo['files'];
if (!empty($files[$fileGrpImages])) {
$file = $specificDoc->getFileInfo($files[$fileGrpImages]);
$image['url'] = $file['location'];
$image['mimetype'] = $file['mimeType'];

// Only deliver static images via the internal PageViewProxy.
// (For IIP and IIIF, the viewer needs to build and access a separate metadata URL, see `getMetadataURL` in `OLSources.js`.)
if ($this->settings['useInternalProxy'] && !str_contains(strtolower($image['mimetype']), 'application')) {
$this->configureProxyUrl($image['url']);
}
break;
} else {
$this->logger->notice('No image file found for page "' . $page . '" in fileGrp "' . $fileGrpImages . '"');
}

} else {

// Get image link.
$physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
$files = $physicalStructureInfo['files'];
if (!empty($files[$fileGrpImages])) {
$file = $this->document->getCurrentDocument()->getFileInfo($files[$fileGrpImages]);
$image['url'] = $file['location'];
$image['mimetype'] = $file['mimeType'];

// Only deliver static images via the internal PageViewProxy.
// (For IIP and IIIF, the viewer needs to build and access a separate metadata URL, see `getMetadataURL` in `OLSources.js`.)
if ($this->settings['useInternalProxy'] && !str_contains(strtolower($image['mimetype']), 'application')) {
$this->configureProxyUrl($image['url']);
}
break;
} else {
$this->logger->notice('No image file found for page "' . $page . '" in fileGrp "' . $fileGrpImages . '"');
foreach ($fileGrpsImages as $fileGrpImages) {
// Get file info for the specific page and file group
$file = $this->fetchFileInfo($page, $fileGrpImages, $specificDoc);
if ($file && Helper::filterFilesByMimeType($file, ['image', 'application'], ['IIIF', 'IIP', 'ZOOMIFY'], 'mimeType')) {
$image['url'] = $file['location'];
$image['mimetype'] = $file['mimeType'];

// Only deliver static images via the internal PageViewProxy.
// (For IIP and IIIF, the viewer needs to build and access a separate metadata URL, see `getMetadataURL` in `OLSources.js`.)
if ($this->settings['useInternalProxy'] && !Helper::filterFilesByMimeType($file, ['application'], ['IIIF', 'IIP', 'ZOOMIFY'], 'mimeType')) {
$this->configureProxyUrl($image['url']);
}
break;
} else {
$this->logger->notice('No image file found for page "' . $page . '" in fileGrp "' . $fileGrpImages . '"');
}
}

if (empty($image)) {
$this->logger->warning('No image file found for page "' . $page . '" in fileGrps "' . $this->extConf['files']['fileGrpImages'] . '"');
}

return $image;
}

/**
* Fetch file info for a specific page and file group.
*
* @param int $page Page number
* @param string $fileGrpImages File group
* @param ?MetsDocument $specificDoc Optional specific document
*
* @return array|null File info array or null if not found
*/
private function fetchFileInfo(int $page, string $fileGrpImages, ?MetsDocument $specificDoc): ?array
{
// Get the physical structure info for the specified page
if ($specificDoc) {
$physicalStructureInfo = $specificDoc->physicalStructureInfo[$specificDoc->physicalStructure[$page]];
} else {
$physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
}

// Get the files for the specified file group
$files = $physicalStructureInfo['files'] ?? null;
if ($files && !empty($files[$fileGrpImages])) {
// Get the file info for the specified file group
if ($specificDoc) {
return $specificDoc->getFileInfo($files[$fileGrpImages]);
} else {
return $this->document->getCurrentDocument()->getFileInfo($files[$fileGrpImages]);
}
}

return null;
}
}
53 changes: 2 additions & 51 deletions Classes/Controller/ToolboxController.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,37 +268,6 @@ public function renderScoreTool()
}
}

/**
* List of common web image mimetypes
* The MIMETYPE attribute must specify the media type of the digital representation. All web-compatible formats as per RFC2046 are allowed.
*/
private const IMAGE_MIMETYPES = [
"image/jpeg",
"image/jpg",
"image/png",
"image/gif",
"image/bmp",
"image/tiff",
"image/x-tiff",
"image/webp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"image/heif",
"image/heic",
"image/vnd.adobe.photoshop",
"image/x-xbitmap",
"image/x-xpixmap",
"image/jp2",
"image/jpx",
"image/jpm",
"image/mj2",
"image/x-portable-anymap",
"image/x-portable-bitmap",
"image/x-portable-graymap",
"image/x-portable-pixmap"
];

/**
* Renders the image download tool (used in template)
*
Expand All @@ -323,38 +292,20 @@ private function renderImageDownloadTool(): void
$imageArray = [];
// Get left or single page download.
$image = $this->getImage($this->requestData['page']);
if ($this->filterImageFiles($image)) {
if (Helper::filterFilesByMimeType($image, ['image'])) {
$imageArray[0] = $image;
}

if ($this->requestData['double'] == 1) {
$image = $this->getImage($this->requestData['page'] + 1);
if ($this->filterImageFiles($image)) {
if (Helper::filterFilesByMimeType($image, ['image'])) {
$imageArray[1] = $image;
}
}

$this->view->assign('imageDownload', $imageArray);
}

/**
* Filters an image file based on its mimetype.
*
* This method checks if the provided image array contains a 'mimetype' key and
* verifies if the mimetype is one of the supported image types defined in the class constant IMAGE_MIMETYPES.
*
* @param mixed $image The image array to filter
*
* @return bool True if the image mimetype is supported, false otherwise
*/
private function filterImageFiles($image): bool
{
if (is_array($image) && isset($image['mimetype'])) {
return in_array($image['mimetype'], self::IMAGE_MIMETYPES);
}
return false;
}

/**
* Get file's URL and MIME type
*
Expand Down

0 comments on commit 881e450

Please sign in to comment.