Search code examples
categoriestypo3-12.xfluid-styled-content

Translated categories not shown in backend selection of category, default fluid-styled-content text & images element and content blocks category field


Steps to reproduce:

  • Create a sysfolder with categories.
  • Create categories
  • Translate sysfolder
  • Translate a category
  • Edit the translated category

Only the default language categories are shown.
The same behavior happens if you create the default Text & Images element of Fluid-Styled-Content.
Another strange behavior seems that the english translation didn´t respect the german main node selection.

I tried this in 2 different installations.
TYPO3 version 12.4.17.

Screenshots:
Main Node English Sub Node English Content element Text & Images English


Solution

  • Maybe this will help someone else, so here are the adjustments that made it work.
    In the backend, I deleted all the category translations and i'm using German (the default language) for the other languages.
    I've now reworked the data processor and also removed all the deprecated features from version 12.

    local_packages/gf_sitepackage/Classes/DataProcessing/FaqProcessor.php

    
    declare(strict_types=1);
    
    namespace Gedankenfolger\GfSitepackage\DataProcessing;
    
    use TYPO3\CMS\Core\Context\Context;
    use TYPO3\CMS\Core\Context\LanguageAspect;
    use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
    use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
    use TYPO3\CMS\Core\Database\ConnectionPool;
    use TYPO3\CMS\Core\Utility\GeneralUtility;
    use TYPO3\CMS\Core\Exception;
    use TYPO3\CMS\Core\Log\LogManager;
    use Psr\Log\LoggerInterface;
    
    /*
     * Gedankenfolger FaqProcessor
     * version 12.0.3
     */
    
    class FaqProcessor implements DataProcessorInterface
    {
        protected Context $context;
        protected ConnectionPool $connectionPool;
        protected LoggerInterface $logger;
    
        /**
         * Constructor to inject necessary dependencies.
         *
         * @param ConnectionPool|null $connectionPool The connection pool for database access.
         * @param Context|null $context The context to retrieve the language aspect and other contextual data.
         * @param LoggerInterface|null $logger The logger for logging errors, if not provided a default logger will be used.
         */
        public function __construct(
            ?ConnectionPool $connectionPool = null,
            ?Context $context = null,
            ?LoggerInterface $logger = null
        ) {
            // Initialize connection pool for database operations, falling back to GeneralUtility if not injected
            $this->connectionPool = $connectionPool ?: GeneralUtility::makeInstance(ConnectionPool::class);
    
            // Initialize context to retrieve language and other contextual data
            $this->context = $context ?: GeneralUtility::makeInstance(Context::class);
    
            // Initialize logger for logging errors, using LogManager if no logger is injected
            $this->logger = $logger ?: GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
        }
    
        /**
         * Process data for FAQ
         *
         * @param ContentObjectRenderer $cObj The data of the content element or page.
         * @param array $contentObjectConfiguration The configuration of Content Object.
         * @param array $processorConfiguration The configuration of this processor (e.g. categories, variable name).
         * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View).
         * @return array The processed data as key/value store.
         * @throws Exception
         */
        public function process(
            ContentObjectRenderer $cObj,
            array $contentObjectConfiguration,
            array $processorConfiguration,
            array $processedData
        ): array {
            try {
                // Retrieve the language aspect from the context
                /** @var LanguageAspect $languageAspect */
                $languageAspect = $this->context->getAspect('language');
    
                // Determine the sys_language_uid (either default language or specific translation)
                $sys_language_uid = $languageAspect->getId();
    
                // Determine the UID of the content element or its parent in case of translations
                $contentElementUid = ($sys_language_uid === 0) ? $processedData["data"]["uid"]: $processedData["data"]["l18n_parent"];
    
                // Get the variable name from the processor configuration (default is 'faqs')
                $variableName = $processorConfiguration["as"] ?? "faqs";
    
                // If categories are specified in the processor configuration, convert to an array
                // Otherwise, fetch the associated categories for the current content element
                $categories = $processorConfiguration["categories"] ?? "";
    
                // If categories are explicitly provided, split them into an array of integers
                if (!empty($categories)) {
                    $categoryIds = GeneralUtility::intExplode(",", $categories, true);
                } else {
                    // Fetch category IDs for the current content element from sys_category_record_mm table
                    $connection = $this->connectionPool->getConnectionForTable("sys_category_record_mm");
    
                    // Create a new QueryBuilder instance for the sys_category_record_mm table
                    $queryBuilder = $connection->createQueryBuilder();
    
                    // Fetch the UID of associated categories based on content element UID and category field
                    $categoryIds = $queryBuilder
                        ->select("uid_local")
                        ->from("sys_category_record_mm")
                        ->where(
                            $queryBuilder->expr()->eq("uid_foreign", $contentElementUid),
                            $queryBuilder->expr()->eq("tablenames", $queryBuilder->createNamedParameter("tt_content")),
                            $queryBuilder->expr()->eq("fieldname", $queryBuilder->createNamedParameter("gedankenfolger_faqfe_categories"))
                        )
                        ->executeQuery()
                        ->fetchFirstColumn();
                }
    
                // Check if there are any categories associated with the content element
                if (!empty($categoryIds)) {
                    // Fetch FAQ IDs associated with the selected categories
                    $connection = $this->connectionPool->getConnectionForTable("sys_category_record_mm");
    
                    // Create a new QueryBuilder instance for sys_category_record_mm table
                    $faqIdsQueryBuilder = $connection->createQueryBuilder();
    
                    // Fetch the foreign UIDs (FAQ IDs) from sys_category_record_mm table
                    $faqIds = $faqIdsQueryBuilder
                        ->select("uid_foreign")
                        ->from("sys_category_record_mm")
                        ->where(
                            $faqIdsQueryBuilder->expr()->eq("tablenames", $faqIdsQueryBuilder->createNamedParameter("tx_gedankenfolger_faqs")),
                            $faqIdsQueryBuilder->expr()->eq("fieldname", $faqIdsQueryBuilder->createNamedParameter("categories")),
                            $faqIdsQueryBuilder->expr()->in("uid_local", $categoryIds)
                        )
                        ->executeQuery()
                        ->fetchFirstColumn();
    
                    // Only fetch FAQs if there are matching FAQ IDs
                    if (!empty($faqIds)) {
                        // Query the FAQ table for the actual FAQ records matching the IDs
                        $faqQueryBuilder = $this->connectionPool->getQueryBuilderForTable("tx_gedankenfolger_faqs");
    
                        // Determine the correct column to filter based on the language (default language uses 'uid', translations use 'l10n_parent')
                        $idColumn = $sys_language_uid === 0 ? 'uid' : 'l10n_parent';
    
                        // Build and execute the query to fetch the FAQ records
                        $faqs = $faqQueryBuilder
                            ->select("*")
                            ->from("tx_gedankenfolger_faqs")
                            ->where(
                                $faqQueryBuilder->expr()->in($idColumn, $faqIds),
                                $faqQueryBuilder->expr()->eq("sys_language_uid", $sys_language_uid),
                                $faqQueryBuilder->expr()->eq("deleted", 0),
                                $faqQueryBuilder->expr()->eq("hidden", 0),
                                $faqQueryBuilder->expr()->lte("starttime", time()),
                                $faqQueryBuilder->expr()->or(
                                    $faqQueryBuilder->expr()->eq("endtime", 0),
                                    $faqQueryBuilder->expr()->gt("endtime", time())
                                )
                            )
                            ->executeQuery()
                            ->fetchAllAssociative();
    
                        // Add the fetched FAQs to the processed data under the specified variable name
                        $processedData[$variableName] = $faqs;
                    } else {
                        // If no FAQ IDs are found, return an empty array for the FAQs
                        $processedData[$variableName] = [];
                    }
                } else {
                    // If no categories are associated, return an empty array for the FAQs
                    $processedData[$variableName] = [];
                }
            } catch (\Doctrine\DBAL\Exception $e) {
                // Log database-related errors
                $this->logger->error('Database error while processing FAQ data', ['exception' => $e]);
                throw new Exception('A database error occurred while processing FAQ data.', 1476107295, $e);
            } catch (\Exception $e) {
                // Log general exceptions
                $this->logger->error('Error processing FAQ data', ['exception' => $e]);
                throw new Exception("Error processing FAQ data: " . $e->getMessage(), 1476107295, $e);
            }
    
            return $processedData;
        }
    }
    

    local_packages/gf_sitepackage/Configuration/Services.yaml

      Gedankenfolger\GfSitepackage\DataProcessing\FaqProcessor:
        autowire: true
        arguments:
          $context: '@TYPO3\CMS\Core\Context\Context'
          $connectionPool: '@TYPO3\CMS\Core\Database\ConnectionPool'
          $logger: '@TYPO3\CMS\Core\Log\LogManager'