From 7ae7e8c4a8abfa69912b408cd2478bd7cba9e733 Mon Sep 17 00:00:00 2001 From: Christoph Lehmann Date: Fri, 9 Sep 2022 07:48:12 +0200 Subject: [PATCH] [FEATURE] Extended filters Extended filters can be defined in UserTsConfig. For demonstration there are examples that can be included with @import 'EXT:pagetreefilter/Configuration/TsConfig/Examples/*.tsconfig' Relates: #16 --- Classes/Controller/TreeController.php | 10 ++- Classes/Controller/WizardController.php | 22 ++++++ .../Domain/Repository/PageTreeRepository.php | 53 +++++++++++-- Classes/Utility/ConfigurationUtility.php | 22 ++++++ Configuration/TsConfig/Examples/Seo.tsconfig | 40 ++++++++++ .../TsConfig/Examples/SolrIndexQueue.tsconfig | 75 +++++++++++++++++++ Resources/Private/Language/de.locallang.xlf | 4 + Resources/Private/Language/locallang.xlf | 3 + .../Public/Css/Backend/pagetreefilter.css | 5 +- 9 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 Configuration/TsConfig/Examples/Seo.tsconfig create mode 100644 Configuration/TsConfig/Examples/SolrIndexQueue.tsconfig diff --git a/Classes/Controller/TreeController.php b/Classes/Controller/TreeController.php index 0c7a8e0..b48ffe2 100644 --- a/Classes/Controller/TreeController.php +++ b/Classes/Controller/TreeController.php @@ -20,10 +20,14 @@ public function filterDataAction(ServerRequestInterface $request): ResponseInter $rootElement['name'] = sprintf('❌ %s', $this->getLanguageService()->sL('LLL:EXT:pagetreefilter/Resources/Private/Language/locallang.xlf:filter_error')); $elements = [$rootElement]; } else { - if (PageTreeRepository::$filteredPageUids !== []) { - foreach($elements as $key => $element) { - if (in_array($element['identifier'], PageTreeRepository::$filteredPageUids)) { + foreach ($elements as $key => $element) { + foreach (PageTreeRepository::$resultSets as $resultSet) { + if (in_array($element['identifier'], $resultSet['pageUids'])) { + if (isset($resultSet['description'])) { + $elements[$key]['tip'] .= sprintf(', %s', $resultSet['description']); + } $elements[$key]['class'] = 'pagetreefilter-highlighted'; + $elements[$key]['backgroundColor'] = $resultSet['backgroundColor'] ?? '#0078e6'; } } } diff --git a/Classes/Controller/WizardController.php b/Classes/Controller/WizardController.php index 8a9a203..51419cc 100644 --- a/Classes/Controller/WizardController.php +++ b/Classes/Controller/WizardController.php @@ -51,6 +51,8 @@ public function getWizards(): array $wizards = $this->appendRecords($wizards); $wizards = $this->appendPageTypes($wizards); $wizards = $this->appendUnknownContentTypes($wizards); + $wizards = $this->appendExtended($wizards); + return $wizards; } @@ -241,6 +243,26 @@ protected function appendUnknownContentTypes(array $wizards): array return $wizards; } + protected function appendExtended($wizards) + { + $customWizardItems = ConfigurationUtility::getCustomWizardItems(); + if ($customWizardItems !== []) { + $wizards['filters']['header'] = + $this->getLanguageService()->sL('LLL:EXT:pagetreefilter/Resources/Private/Language/locallang.xlf:wizard_tab_extended_filters'); + + foreach ($customWizardItems as $identifier => $wizardItem) { + $wizards['filters_' . $identifier] = [ + 'title' => $this->getLanguageService()->sL($wizardItem['title']), + 'description' => $this->getLanguageService()->sL($wizardItem['description'] ?? ''), + 'iconIdentifier' => $wizardItem['iconIdentifier'] ?? 'actions-filter', + 'filter' => $wizardItem['filter'] + ]; + } + } + + return $wizards; + } + protected function areRecordsInTable($tableName): bool { /** @var QueryBuilder $queryBuilder */ diff --git a/Classes/Domain/Repository/PageTreeRepository.php b/Classes/Domain/Repository/PageTreeRepository.php index 03368a1..dd07573 100644 --- a/Classes/Domain/Repository/PageTreeRepository.php +++ b/Classes/Domain/Repository/PageTreeRepository.php @@ -13,7 +13,7 @@ class PageTreeRepository extends \TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository { - public static $filteredPageUids = []; + public static $resultSets = []; public static $filterErrorneous = false; @@ -21,7 +21,8 @@ class PageTreeRepository extends \TYPO3\CMS\Backend\Tree\Repository\PageTreeRepo protected $filterConstraints = []; - // @todo: Use predefined list from core. Where is it? + protected $extendedFiltersThatShouldBeApplied = []; + protected const ALLOWED_TABLE_FIELDS = [ 'tt_content:CType', 'tt_content:list_type', @@ -35,19 +36,52 @@ public function fetchFilteredTree(string $searchFilter, array $allowedMountPoint { if (ConfigurationUtility::isWizardEnabled()) { $newSearchFilter = $this->extractConstraints($searchFilter); + $searchFilter = $newSearchFilter; + $pages = []; + if ($this->filterTable) { $this->validate(); if (!self::$filterErrorneous) { - self::$filteredPageUids = $this->getFilteredPageUids(); + $pages = $this->getFilteredPageUids(); + self::$resultSets['default'] = [ + 'pageUids' => $pages + ]; + } + } - if (self::$filteredPageUids !== []) { - $additionalWhereClause = sprintf('%s AND uid IN (%s)', $additionalWhereClause, - implode(',', self::$filteredPageUids)); - $searchFilter = $newSearchFilter; + $extendedFilters = ConfigurationUtility::getExtendedFilters(); + if ($this->extendedFiltersThatShouldBeApplied !== [] && $extendedFilters !== []) { + foreach($this->extendedFiltersThatShouldBeApplied as $extendedFilter) { + if (isset($extendedFilters[$extendedFilter])) { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($extendedFilters[$extendedFilter]['select.']['table']); + $filteredPages = $queryBuilder + ->select($extendedFilters[$extendedFilter]['select.']['field']) + ->from($extendedFilters[$extendedFilter]['select.']['table']) + ->andWhere($extendedFilters[$extendedFilter]['select.']['where']) + ->execute() + ->fetchFirstColumn(); + + self::$resultSets[$extendedFilter] = array_merge( + $extendedFilters[$extendedFilter], + [ + 'pageUids' => $filteredPages + ] + ); + + foreach ($filteredPages as $filteredPage) { + $pages[] = $filteredPage; + } } } } + + + if ($pages !== []) { + $additionalWhereClause = sprintf('%s AND uid IN (%s)', $additionalWhereClause, + implode(',', array_unique($pages))); + } } return parent::fetchFilteredTree($searchFilter, $allowedMountPointPageIds, $additionalWhereClause); @@ -117,6 +151,9 @@ protected function extractConstraints(string $searchFilter): string case 'table': $this->filterTable = $filter[1]; break; + case 'filter': + $this->extendedFiltersThatShouldBeApplied[] = $filter[1]; + break; default: $this->filterConstraints[] = [ 'field' => $filter[0], @@ -140,7 +177,7 @@ protected function validate() if (!$backendUser->isAdmin() && !$backendUser->check('tables_select', $this->filterTable)) { self::$filterErrorneous = true; } - $connection = $connection = GeneralUtility::makeInstance(ConnectionPool::class) + $connection = GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable($this->filterTable); foreach($this->filterConstraints as $constraint) { if (!isset($GLOBALS['TCA'][$this->filterTable]['columns'][$constraint['field']])) { diff --git a/Classes/Utility/ConfigurationUtility.php b/Classes/Utility/ConfigurationUtility.php index 53b15ce..d3a4b58 100644 --- a/Classes/Utility/ConfigurationUtility.php +++ b/Classes/Utility/ConfigurationUtility.php @@ -39,6 +39,28 @@ public static function getPageId() return GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('pagetreefilter', 'pageId'); } + public static function getExtendedFilters(): array + { + $backendUser = self::getBackendUser(); + $filters = $backendUser->getTSConfig()['tx_pagetreefilter.']['filters.'] ?? []; + foreach ($filters as $name => $configuration) { + $filters[rtrim($name, '.')] = $configuration; + unset($filters[$name]); + } + return $filters; + } + + public static function getCustomWizardItems(): array + { + $backendUser = self::getBackendUser(); + $wizardItems = $backendUser->getTSConfig()['tx_pagetreefilter.']['wizardItems.'] ?? []; + foreach ($wizardItems as $name => $configuration) { + $wizardItems[rtrim($name, '.')] = $configuration; + unset($wizardItems[$name]); + } + return $wizardItems; + } + protected static function getBackendUser(): ?BackendUserAuthentication { return $GLOBALS['BE_USER']; diff --git a/Configuration/TsConfig/Examples/Seo.tsconfig b/Configuration/TsConfig/Examples/Seo.tsconfig new file mode 100644 index 0000000..52fc931 --- /dev/null +++ b/Configuration/TsConfig/Examples/Seo.tsconfig @@ -0,0 +1,40 @@ +tx_pagetreefilter { + filters { + indexingEnabled { + description = Page will be indexed by search engines + select { + field = uid + table = pages + where = no_index=0 AND doktype < 199 + } + backgroundColor = green + } + indexingDisabled { + description = Page will be not be indexed by search engines + select { + field = uid + table = pages + where = no_index=1 AND doktype < 199 + } + backgroundColor = grey + } + noMetaDescription { + description = Page has no meta description, so the description in search engine results might be inaccurate + select { + field = uid + table = pages + where = no_index=0 AND doktype < 199 AND (description='' OR description IS NULL) + } + backgroundColor = red + } + } + + wizardItems { + seoStatus { + title = SEO Status + description = Show pages with SEO issues + iconIdentifier = content-elements-searchform + filter = filter=indexingEnabled filter=indexingDisabled filter=noMetaDescription + } + } +} \ No newline at end of file diff --git a/Configuration/TsConfig/Examples/SolrIndexQueue.tsconfig b/Configuration/TsConfig/Examples/SolrIndexQueue.tsconfig new file mode 100644 index 0000000..6f9f5f0 --- /dev/null +++ b/Configuration/TsConfig/Examples/SolrIndexQueue.tsconfig @@ -0,0 +1,75 @@ +tx_pagetreefilter { + filters { + indexedInSolr { + # The description is visible when you hover over the page icon + description = Indexed in Solr + + select { + field = item_uid + table = tx_solr_indexqueue_item + where = indexing_configuration = 'pages' AND indexed > changed AND errors = '' + } + + # A CSS background color for pages matching the query + backgroundColor = darkgreen + } + solrIndexingErrors { + description = Has indexing error + select { + field = item_uid + table = tx_solr_indexqueue_item + where = indexing_configuration = 'pages' AND errors != '' + } + backgroundColor = red + } + solrNeedsIndexing { + description = Needs (re)indexing + select { + field = item_uid + table = tx_solr_indexqueue_item + where = indexing_configuration = 'pages' AND indexed = 0 AND errors = '' + } + backgroundColor = lightgreen + } + solrNotInQueue { + description = Not in Index Queue + select { + field = uid + table = pages + where = sys_language_uid = 0 AND uid NOT IN (SELECT item_uid FROM tx_solr_indexqueue_item) + } + backgroundColor = orange + } + solrNoSearch { + description = Indexing disabled + select { + field = uid + table = pages + where = no_search = 1 + } + backgroundColor = lightgrey + } + solrNoSearchSubEntries { + description = Indexing disabled for sub entries + select { + field = uid + table = pages + where = no_search_sub_entries = 1 + } + backgroundColor = #4b4b4b + } + } + + wizardItems { + solrIndexQueueStatus { + title = Solr Index Queue Status + description = Show pages that are indexed, not in the queue or have errors + + # Overview of icons in TYPO3: https://typo3.github.io/TYPO3.Icons/ + iconIdentifier = content-elements-searchform + + # Multiple filters can be combined. The order matters for overlapping queries with different background colors + filter = filter=indexedInSolr filter=solrIndexingErrors filter=solrNeedsIndexing filter=solrNotInQueue filter=solrNoSearch filter=solrNoSearchSubEntries + } + } +} \ No newline at end of file diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf index b5edbff..88d16a8 100644 --- a/Resources/Private/Language/de.locallang.xlf +++ b/Resources/Private/Language/de.locallang.xlf @@ -15,6 +15,10 @@ Records Datensätze + + Extended Filters + Erweiterte Filter + Nothing found. Probably the page id was configured incorrectly. Nichts gefunden. Wahrscheinlich ist die Seiten-Id falsch konfiguriert. diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 07385a0..b8183a1 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -12,6 +12,9 @@ Records + + Extended Filters + Nothing found. Probably the page id was configured incorrectly. diff --git a/Resources/Public/Css/Backend/pagetreefilter.css b/Resources/Public/Css/Backend/pagetreefilter.css index 0138425..2a5be44 100644 --- a/Resources/Public/Css/Backend/pagetreefilter.css +++ b/Resources/Public/Css/Backend/pagetreefilter.css @@ -1,13 +1,12 @@ .pagetreefilter-highlighted { - fill: #0078e6 !important; - fill-opacity: 0.1; + fill-opacity: 0.3; stroke-width: 1px; stroke: #d7d7d7; } .pagetreefilter-highlighted.node-over, .pagetreefilter-highlighted.node-selected { - fill-opacity: 0.3; + fill-opacity: 0.6; } .pagetreefilter-wizard .modal-body {