diff --git a/Classes/DataProcessor/GalleryDataProcessor.php b/Classes/DataProcessor/GalleryDataProcessor.php
new file mode 100644
index 00000000..ce7ea69c
--- /dev/null
+++ b/Classes/DataProcessor/GalleryDataProcessor.php
@@ -0,0 +1,114 @@
+stdWrapValue(
+ 'filesProcessedDataKey',
+ $processorConfiguration,
+ 'files',
+ );
+
+ if (isset($processedData[$filesProcessedDataKey]) && is_array($processedData[$filesProcessedDataKey])) {
+ /** @var FileReference[] $fileObjects */
+ $fileObjects = $processedData[$filesProcessedDataKey];
+ } else {
+ throw new ContentRenderingException(
+ 'No files found for key ' . $filesProcessedDataKey . ' in $processedData.',
+ 1779280032,
+ );
+ }
+
+ $requiredColumns = $this->getRequiredColumns();
+
+ if ($requiredColumns !== []) {
+ foreach ($fileObjects as $key => $fileObject) {
+ if (!$this->isValidFileObject($fileObject, $requiredColumns)) {
+ unset($fileObjects[$key]);
+ }
+ }
+
+ $processedData[$filesProcessedDataKey] = $fileObjects;
+ }
+
+ return $this->originalGalleryProcessor->process(
+ $cObj,
+ $contentObjectConfiguration,
+ $processorConfiguration,
+ $processedData,
+ );
+ }
+
+ private function isValidFileObject(FileReference $fileObject, array $requiredColumns): bool
+ {
+ // Only check image files here
+ if ($fileObject->getType() !== 2) {
+ return true;
+ }
+
+ foreach ($requiredColumns as $requiredColumn) {
+ if (!$fileObject->hasProperty($requiredColumn)) {
+ return false;
+ }
+
+ $value = $fileObject->getProperty($requiredColumn);
+ $trimmedValue = is_string($value) ? trim($value) : $value;
+
+ if ($trimmedValue === null || $trimmedValue === '') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function getRequiredColumns(): array
+ {
+ $requiredColumns = [];
+
+ try {
+ $requiredColumns = GeneralUtility::trimExplode(
+ ',',
+ $this->extensionConfiguration->get('jwtools2', 'typo3RequiredColumnsForFiles'),
+ true,
+ );
+ } catch (ExtensionConfigurationExtensionNotConfiguredException|ExtensionConfigurationPathDoesNotExistException) {
+ }
+
+ return $requiredColumns;
+ }
+}
diff --git a/Classes/EventListener/AddMandatoryControlForFileReferencesEventListener.php b/Classes/EventListener/AddMandatoryControlForFileReferencesEventListener.php
new file mode 100644
index 00000000..b73b3060
--- /dev/null
+++ b/Classes/EventListener/AddMandatoryControlForFileReferencesEventListener.php
@@ -0,0 +1,128 @@
+getRecord();
+ $requiredColumns = $this->getRequiredColumns();
+ if ($requiredColumns === []) {
+ return;
+ }
+
+ // Only check image files here
+ if ((int)($record['type'] ?? 0) !== 2) {
+ return;
+ }
+
+ foreach ($requiredColumns as $requiredColumn) {
+ $value = $record['uid_local'][0][$requiredColumn] ?? $record[$requiredColumn] ?? '';
+ $trimmedValue = is_string($value) ? trim($value) : $value;
+
+ if ($trimmedValue === null || $trimmedValue === '') {
+ $event->setControl(
+ 'requiredColumns',
+ '',
+ );
+ return;
+ }
+ }
+ }
+
+ private function getRequiredColumns(): array
+ {
+ $requiredColumns = [];
+
+ try {
+ $requiredColumns = GeneralUtility::trimExplode(
+ ',',
+ $this->extensionConfiguration->get('jwtools2', 'typo3RequiredColumnsForFiles'),
+ true,
+ );
+ } catch (ExtensionConfigurationExtensionNotConfiguredException|ExtensionConfigurationPathDoesNotExistException) {
+ }
+
+ return $requiredColumns;
+ }
+
+ protected function getMandatoryMessage(array $requiredColumns): string
+ {
+ return LocalizationUtility::translate(
+ 'LLL:EXT:jwtools2/Resources/Private/Language/locallang_mod.xlf:warning.requiredColumns',
+ 'Jwtools2',
+ [
+ implode(
+ ', ',
+ $this->getTranslatedColumnNames(
+ $requiredColumns,
+ ),
+ ),
+ ],
+ );
+ }
+
+ protected function getTranslatedColumnNames(array $requiredColumns): array
+ {
+ if (!$this->getLanguageService() instanceof LanguageService) {
+ return $requiredColumns;
+ }
+
+ foreach ($requiredColumns as $key => $requiredColumn) {
+ $label = BackendUtility::getItemLabel('sys_file', $requiredColumn);
+ if ($label === '' || $label === null) {
+ $label = BackendUtility::getItemLabel('sys_file_metadata', $requiredColumn);
+ }
+
+ if ($label === '' || $label === null) {
+ continue;
+ }
+
+ $translatedLabel = $this->getLanguageService()->sL($label);
+ if ($translatedLabel === '') {
+ continue;
+ }
+
+ $requiredColumns[$key] = $translatedLabel;
+ }
+
+ return $requiredColumns;
+ }
+
+ protected function getLanguageService(): LanguageService
+ {
+ return $GLOBALS['LANG'];
+ }
+}
diff --git a/Classes/EventListener/IsFileSelectableEventListener.php b/Classes/EventListener/IsFileSelectableEventListener.php
deleted file mode 100644
index deebe418..00000000
--- a/Classes/EventListener/IsFileSelectableEventListener.php
+++ /dev/null
@@ -1,209 +0,0 @@
-getRequiredColumnsFromExtensionConfiguration()) {
- $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
- $flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
- $message = $this->getFlashMessageDescription($requiredColumns);
-
- if (!$this->checkMessageExists($flashMessageQueue, $message)) {
- $this->addFlashMessage($flashMessageQueue, $message);
- }
-
- if (!$event->getFile() instanceof File) {
- // Do not process folders or processed files
- return;
- }
-
- if ($event->getFile()->getType() !== 2) {
- // Process only images
- return;
- }
-
- foreach ($this->getRequiredColumnsForFileMetaData() as $requiredColumn) {
- $properties = $event->getFile()->getProperties();
-
- // Do not use isset() as "null" values have to be tested, too.
- if (!array_key_exists($requiredColumn, $properties)) {
- $event->denyFileSelection();
- }
-
- $value = is_string($properties[$requiredColumn])
- ? trim($properties[$requiredColumn])
- : $properties[$requiredColumn];
-
- if (!isset($value) || trim($value) === null || trim($value) === '') {
- $event->denyFileSelection();
- }
- }
- }
- }
-
- protected function addFlashMessage(FlashMessageQueue $flashMessageQueue, string $message): void
- {
- $flashMessageQueue->addMessage(
- GeneralUtility::makeInstance(
- FlashMessage::class,
- $message,
- LocalizationUtility::translate(
- 'LLL:EXT:jwtools2/Resources/Private/Language/locallang_mod.xlf:fileBrowser.flashMessage.requiredColumns.title',
- ),
- ContextualFeedbackSeverity::INFO,
- ),
- );
- }
-
- protected function getFlashMessageDescription(array $requiredColumns): string
- {
- return LocalizationUtility::translate(
- 'LLL:EXT:jwtools2/Resources/Private/Language/locallang_mod.xlf:fileBrowser.flashMessage.requiredColumns.description',
- null,
- [
- implode(
- ', ',
- $this->getTranslatedColumnNames(
- $requiredColumns,
- ),
- ),
- ],
- );
- }
-
- protected function getTranslatedColumnNames(array $requiredColumns): array
- {
- if (!$this->getLanguageService() instanceof LanguageService) {
- return $requiredColumns;
- }
-
- foreach ($requiredColumns as $key => $requiredColumn) {
- $label = BackendUtility::getItemLabel('sys_file', $requiredColumn);
- if ($label === '' || $label === null) {
- $label = BackendUtility::getItemLabel('sys_file_metadata', $requiredColumn);
- }
-
- if ($label === '' || $label === null) {
- continue;
- }
-
- $translatedLabel = $this->getLanguageService()->sL($label);
- if ($translatedLabel === '' || $translatedLabel === null) {
- continue;
- }
-
- $requiredColumns[$key] = $translatedLabel;
- }
-
- return $requiredColumns;
- }
-
- protected function getRequiredColumnsForFileMetaData(): array
- {
- // Cache result, is this method will be called from within a loop
- static $requiredColumns = null;
-
- if ($requiredColumns === null) {
- $validColumns = [];
- foreach ($this->getRequiredColumnsFromExtensionConfiguration() as $column) {
- if ($this->isValidColumn($column)) {
- $validColumns[] = $column;
- }
- }
-
- $requiredColumns = $validColumns;
- }
-
- return $requiredColumns;
- }
-
- protected function getRequiredColumnsFromExtensionConfiguration(): array
- {
- static $requiredColumns = null;
-
- if ($requiredColumns === null) {
- try {
- $requiredColumns = GeneralUtility::trimExplode(
- ',',
- $this->extensionConfiguration->get('jwtools2', 'typo3RequiredColumnsForFiles'),
- true,
- );
- } catch (ExtensionConfigurationExtensionNotConfiguredException|ExtensionConfigurationPathDoesNotExistException $exception) {
- }
- }
-
- return $requiredColumns;
- }
-
- protected function isValidColumn(string $column, string $table = 'sys_file'): bool
- {
- $columnExists = false;
- $connection = $this->getConnectionPool()->getConnectionForTable($table);
- $schemaManager = $connection->createSchemaManager();
- if (
- $schemaManager->tablesExist([$table])
- ) {
- $columnExists = $schemaManager->introspectTable($table)->hasColumn($column);
- if ($columnExists === false && $table !== 'sys_file_metadata') {
- $columnExists = $this->isValidColumn($column, 'sys_file_metadata');
- }
- }
-
- return $columnExists;
- }
-
- protected function checkMessageExists(FlashMessageQueue $flashMessageQueue, string $message): bool
- {
- $messageExists = false;
- foreach ($flashMessageQueue as $flashMessage) {
- if ($flashMessage->getMessage() === $message) {
- $messageExists = true;
- break;
- }
- }
-
- return $messageExists;
- }
-
- protected function getLanguageService(): LanguageService
- {
- return $GLOBALS['LANG'];
- }
-
- protected function getConnectionPool(): ConnectionPool
- {
- return GeneralUtility::makeInstance(ConnectionPool::class);
- }
-}
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
index 3b868a50..e078cbf0 100644
--- a/Configuration/Services.yaml
+++ b/Configuration/Services.yaml
@@ -7,45 +7,48 @@ services:
JWeiland\Jwtools2\:
resource: '../Classes/*'
- # Called by makeInstance in getCallableFromTarget of Dispatcher
- JWeiland\Jwtools2\Controller\Ajax\SysFileController:
- public: true
-
JWeiland\Jwtools2\Command\CacheQueryCommand:
tags:
- - name: 'console.command'
- command: 'jwtools2:cacheQuery'
- schedulable: false
+ - name: 'console.command'
+ command: 'jwtools2:cacheQuery'
+ schedulable: false
+
JWeiland\Jwtools2\Command\ConvertPlainPasswordToHashCommand:
tags:
- - name: 'console.command'
- command: 'jwtools2:convertpasswords'
- schedulable: false
+ - name: 'console.command'
+ command: 'jwtools2:convertpasswords'
+ schedulable: false
+
JWeiland\Jwtools2\Command\StatusReportCommand:
calls:
- method: setTaskRepository
arguments:
- '@TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository'
tags:
- - name: 'console.command'
- command: 'jwtools2:statusreport'
- schedulable: false
+ - name: 'console.command'
+ command: 'jwtools2:statusreport'
+ schedulable: false
- # Called by makeInstance in start() of ContentObjectRenderer
- JWeiland\Jwtools2\Hooks\InitializeStdWrap:
- public: true
+ # Backend Context Menu Provider
+ JWeiland\Jwtools2\ContextMenu\ItemProviders\UpdateFileMetaDataProvider:
+ tags:
+ - name: backend.contextmenu.itemprovider
+ identifier: 'jwtools2.contextmenu.itemproviders.updatefilemetadata'
- JWeiland\Jwtools2\LinkHandler\FileLinkHandler:
+ # Called by makeInstance in getCallableFromTarget of Dispatcher
+ JWeiland\Jwtools2\Controller\Ajax\SysFileController:
public: true
- JWeiland\Jwtools2\LinkHandler\FolderLinkHandler:
- public: true
+ JWeiland\Jwtools2\DataProcessor\GalleryDataProcessor:
+ shared: false
+ tags:
+ - { name: 'data.processor', identifier: 'gallery' }
# Event Listeners
JWeiland\Jwtools2\EventListener\ReduceCategoryTreeToPageTree:
tags:
- - name: event.listener
- event: TYPO3\CMS\Core\Tree\Event\ModifyTreeDataEvent
+ - name: event.listener
+ event: TYPO3\CMS\Core\Tree\Event\ModifyTreeDataEvent
JWeiland\Jwtools2\EventListener\IndexServiceEventListener:
tags:
@@ -53,14 +56,12 @@ services:
identifier: 'jwtools2.events.beforeItemIsIndexedEvent'
event: ApacheSolrForTypo3\Solr\Event\Indexing\BeforeItemIsIndexedEvent
- JWeiland\Jwtools2\EventListener\IsFileSelectableEventListener:
- tags:
- - name: event.listener
- identifier: 'jwtools2.events.isFileSelectableEvent'
- event: TYPO3\CMS\Backend\ElementBrowser\Event\IsFileSelectableEvent
+ # Called by makeInstance in start() of ContentObjectRenderer
+ JWeiland\Jwtools2\Hooks\InitializeStdWrap:
+ public: true
- # Backend Context Menu Provider
- JWeiland\Jwtools2\ContextMenu\ItemProviders\UpdateFileMetaDataProvider:
- tags:
- - name: backend.contextmenu.itemprovider
- identifier: 'jwtools2.contextmenu.itemproviders.updatefilemetadata'
+ JWeiland\Jwtools2\LinkHandler\FileLinkHandler:
+ public: true
+
+ JWeiland\Jwtools2\LinkHandler\FolderLinkHandler:
+ public: true
diff --git a/Documentation/AdministratorManual/Upgrade/Index.rst b/Documentation/AdministratorManual/Upgrade/Index.rst
index ad2b402b..7bc5edbf 100644
--- a/Documentation/AdministratorManual/Upgrade/Index.rst
+++ b/Documentation/AdministratorManual/Upgrade/Index.rst
@@ -8,6 +8,23 @@ Upgrade
If you upgrade EXT:jwtools2 to a newer version, please read this section
carefully!
+Upgrade to Version 8.1.2
+========================
+
+The IsFileSelectableEventListener class has been removed completely. This is a
+breaking change. However, since the previous implementation required patching the
+TYPO3 Core to make this feature work, removing this class should not cause
+additional issues in practice.
+
+The configuration option `typo3RequiredColumnsForFiles` is still available, but
+the feature has been reimplemented in a different way. Once enabled, the TYPO3
+GalleryProcessor is overridden and all files that do not have the required column
+values assigned are removed from the processed output.
+
+In addition, a new control icon is added to each `sys_file_reference` record in
+backend forms where file references are configured. When hovering over this icon,
+editors can see which required columns still need to be completed.
+
Upgrade to Version 7.0.0
========================
diff --git a/Documentation/Configuration/Index.rst b/Documentation/Configuration/Index.rst
index caeec569..1ea2c506 100644
--- a/Documentation/Configuration/Index.rst
+++ b/Documentation/Configuration/Index.rst
@@ -91,8 +91,9 @@ typo3RequiredColumnsForFiles
Add a comma separated list of column names of table ``sys_file`` or
``sys_file_metadata`` to set these columns as required. If these columns are not
-filled for an image it is not selectable in FileBrowser. It can not be inserted
-into a ContentElement or record.
+filled for an image, the image will not be displayed in the frontend until the
+required fields have been completed.
+
.. _typo3ExcludeVideoFilesFromFalFilter:
diff --git a/Documentation/guides.xml b/Documentation/guides.xml
index 0ae85529..3eca6af4 100644
--- a/Documentation/guides.xml
+++ b/Documentation/guides.xml
@@ -1,33 +1,22 @@
-
-
-
-
-
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="https://www.phpdoc.org/guides ../vendor/phpdocumentor/guides-cli/resources/schema/guides.xsd"
+ links-are-relative="true">
+
+
+
+
+
+
diff --git a/Resources/Private/Language/ExtConf.xlf b/Resources/Private/Language/ExtConf.xlf
index c2f0b95e..6662292f 100644
--- a/Resources/Private/Language/ExtConf.xlf
+++ b/Resources/Private/Language/ExtConf.xlf
@@ -16,9 +16,9 @@
- Required columns for image files: Enter here columns of the DB tables sys_file and/or sys_file_metadata
- which are to be regarded as mandatory fields. Files for which these columns are not set are available in the
- file browser, but cannot be selected.
+ Required columns for image files: Enter columns of the DB tables sys_file and/or sys_file_metadata
+ that should be treated as mandatory fields. Image files for which these columns are not set will not be
+ displayed in the frontend.
diff --git a/Resources/Private/Language/locallang_mod.xlf b/Resources/Private/Language/locallang_mod.xlf
index 83cb5439..807d83a7 100644
--- a/Resources/Private/Language/locallang_mod.xlf
+++ b/Resources/Private/Language/locallang_mod.xlf
@@ -9,14 +9,8 @@
Successfully created/updated file metadata and deleted related processed files.
-
-
- Info about non selectable files
-
-
- For some files, certain mandatory fields (%s) must be filled in. If this is not the case, these files
- cannot currently be linked to content elements or other records. Please complete these mandatory fields first.
-
+
+ For some image files, certain mandatory fields (%s) are missing. These images will not be displayed in the frontend until the required fields have been completed.