From 12905c2750e32af88c54ff1833c722f7ed44c9f9 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Thu, 26 Sep 2024 18:14:35 +0000 Subject: [PATCH 1/9] task: introduce ImageGalleryRepository --- Classes/Controller/GalleryController.php | 4 +- .../Repository/ImageGalleryRepository.php | 23 +++++ Configuration/FlexForm/NaturalGallery.xml | 89 ------------------- 3 files changed, 25 insertions(+), 91 deletions(-) create mode 100644 Classes/Domain/Repository/ImageGalleryRepository.php diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index 50ca237..a7f253f 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -17,7 +17,7 @@ use Fab\NaturalGallery\Domain\Repository\CategoryRepository; use Fab\NaturalGallery\Persistence\MatcherFactory; use Fab\NaturalGallery\Persistence\OrderFactory; -use Fab\Vidi\Domain\Repository\ContentRepositoryFactory; +use Fab\NaturalGallery\Domain\Repository\ImageGalleryRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; @@ -46,7 +46,7 @@ public function listAction() $order = OrderFactory::getInstance()->getOrder($this->settings); // Fetch the adequate repository for a known data type. - $contentRepository = ContentRepositoryFactory::getInstance('sys_file'); + $contentRepository = ImageGalleryRepository::getInstance('sys_file'); // Fetch and count files $images = $contentRepository->findBy($matcher, $order); diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php new file mode 100644 index 0000000..f072201 --- /dev/null +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -0,0 +1,23 @@ +LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.source array - - - - - - input - 40 - trim - - - popup - link_popup.gif - - wizard_element_browser - - wizard - file - - - - page,url,mail,spec,file - - - height=900,width=900,status=0,menubar=0,scrollbars=1 - - - - - - - - - - select - selectSingle - Fab\NaturalGallery\Backend\TceForms->getSelections - 1 - 1 - - - - - - - - input - - - - - - - - - input - - - - - - - - select - selectSingle - - - - - - - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.direction.descending - - DESC - - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.direction.ascending - - ASC - - - 1 - 1 - - - - From 04321b11151a52b6fea9e076e931a4aa13416150 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Fri, 27 Sep 2024 18:18:29 +0000 Subject: [PATCH 2/9] [TASK] Delete settings from Flexform --- Configuration/FlexForm/NaturalGallery.xml | 228 ---------------------- 1 file changed, 228 deletions(-) diff --git a/Configuration/FlexForm/NaturalGallery.xml b/Configuration/FlexForm/NaturalGallery.xml index a626861..b89e0f1 100644 --- a/Configuration/FlexForm/NaturalGallery.xml +++ b/Configuration/FlexForm/NaturalGallery.xml @@ -31,153 +31,6 @@ LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.imageSettings array - - - - - - select - selectSingle - - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.thumbnailFormat.natural - - natural - - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.thumbnailFormat.square - - square - - - 0 - 1 - - - - - - - - check - 1 - - - - - - - - select - selectSingle - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.hover - hover - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.true - true - - - LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.false - false - - - 1 - 1 - - - - - - - - input - 4 - trim - - - - - - - - input - 0 - trim - - - - - - - - input - 1 - trim - - - - - - - - input - trim - - - - - - - - input - 350 - trim - - - - - - - - input - 1600 - trim - - - - - - - - input - trim - - - - @@ -188,87 +41,6 @@ LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.filterBar array - - - - - - check - 0 - - - - - - - - check - 0 - - - - - - - - check - 0 - - - - - - - - check - 0 - - - - - - - - check - 0 - - - - - - - - select - selectMultipleSideBySide - 50 - sys_category - AND sys_category.sys_language_uid IN (-1, 0) ORDER BY sys_category.sorting ASC - 9999 - tree - 10 - - - 1 - 1 - - parent - - - - - From 3791a5a73552e272687c62f460db89fd6a81a5a1 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Thu, 3 Oct 2024 15:49:41 +0000 Subject: [PATCH 3/9] fix: configuration imageGalleryRepository --- .../Repository/ImageGalleryRepository.php | 92 +++++++++++++++---- Classes/Utility/ConfigurationUtility.php | 83 +++++++++++++++++ ext_conf_template.txt | 14 +++ 3 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 Classes/Utility/ConfigurationUtility.php create mode 100644 ext_conf_template.txt diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index f072201..f02f501 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -1,23 +1,81 @@ get($field); + } + + public function findByUid(int $uid): array + { + $query = $this->getQueryBuilder(); + $query + ->select('*') + ->from($this->tableName) + ->where( + $this->getQueryBuilder() + ->expr() + ->eq('uid', $this->getQueryBuilder()->expr()->literal($uid)), + ); + + $messages = $query->execute()->fetchOne(); + + return is_array($messages) ? $messages : []; + } + + /** + * @throws Exception + * @throws DBALException + */ + public function findByDemand(array $demand = [], array $orderings = [], int $offset = 0, int $limit = 0): array + { + $queryBuilder = $this->getQueryBuilder(); + $queryBuilder->select('*')->from($this->tableName); + + $constraints = []; + foreach ($demand as $field => $value) { + $constraints[] = $queryBuilder + ->expr() + ->like( + $field, + $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards($value) . '%'), + ); + } + if ($constraints) { + $queryBuilder->where($queryBuilder->expr()->orX(...$constraints)); + } + + # We handle the sorting + $queryBuilder->addOrderBy(key($orderings), current($orderings)); + + if ($limit > 0) { + $queryBuilder->setMaxResults($limit); + } + + return $queryBuilder->execute()->fetchAllAssociative(); + } + + /** + * @throws DBALException + * @throws Exception + */ + public function findByUids(array $uids): array + { + $query = $this->getQueryBuilder(); + $query + ->select('*') + ->from($this->tableName) + ->where($this->getQueryBuilder()->expr()->in('uid', $uids)); + + return $query->execute()->fetchAllAssociative(); + } + } \ No newline at end of file diff --git a/Classes/Utility/ConfigurationUtility.php b/Classes/Utility/ConfigurationUtility.php new file mode 100644 index 0000000..850a2bc --- /dev/null +++ b/Classes/Utility/ConfigurationUtility.php @@ -0,0 +1,83 @@ +get('natural_gallery'); + + // Fill up configuration array with relevant values. + foreach ($configuration as $key => $value) { + $this->configuration[$key] = $value; + } + + // // Special case for "recipient_data_type" + // if (empty($this->configuration['recipient_data_type'])) { + // $this->configuration['recipient_data_type'] = 'fe_users'; + // } + } + + /** + * Returns a setting key. + * + * @param string $key + * @return mixed + */ + public function get($key) + { + return isset($this->configuration[$key]) ? trim((string) $this->configuration[$key]) : null; + } + + /** + * Set a setting key. + * + * @param string $key + * @param mixed $value + */ + public function set($key, $value): void + { + $this->configuration[$key] = $value; + } + + public function getConfiguration(): array + { + return $this->configuration; + } + +} diff --git a/ext_conf_template.txt b/ext_conf_template.txt new file mode 100644 index 0000000..1c89005 --- /dev/null +++ b/ext_conf_template.txt @@ -0,0 +1,14 @@ +# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. +stockage_directory = + +# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. +constraint = + +# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. +category = + +# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. +sorting = + +# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. +default_direction = \ No newline at end of file From 07fb8c33e701c5f9772c959512fbca24a2276fd9 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Tue, 8 Oct 2024 14:51:59 +0000 Subject: [PATCH 4/9] fix: vidi files refactoring --- Classes/Controller/GalleryController.php | 84 +++- .../Repository/ImageGalleryRepository.php | 101 +++- Classes/Persistence/Matcher.php | 465 ++++++++++++++++++ Classes/Persistence/MatcherFactory.php | 189 +------ Classes/Persistence/OrderFactory.php | 20 +- Classes/Persistence/order.php | 54 ++ Classes/Utility/TcaFieldsUtility.php | 96 ++++ ext_conf_template.txt | 21 +- 8 files changed, 809 insertions(+), 221 deletions(-) create mode 100644 Classes/Persistence/Matcher.php create mode 100644 Classes/Persistence/order.php create mode 100644 Classes/Utility/TcaFieldsUtility.php diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index a7f253f..156aaf7 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -14,23 +14,49 @@ * The TYPO3 project - inspiring people to share! */ -use Fab\NaturalGallery\Domain\Repository\CategoryRepository; +use Fab\NaturalGallery\Domain\Repository\ImageGalleryRepository; use Fab\NaturalGallery\Persistence\MatcherFactory; use Fab\NaturalGallery\Persistence\OrderFactory; -use Fab\NaturalGallery\Domain\Repository\ImageGalleryRepository; +use Fab\NaturalGallery\Utility\ConfigurationUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Extbase\Persistence\QueryInterface; /** * Controller */ class GalleryController extends ActionController { - protected CategoryRepository $categoryRepository; + protected ImageGalleryRepository $galleryRepository; + + protected MatcherFactory $matcherFactory; + protected OrderFactory $orderFactory; protected array $configuration = array(); - protected $settings = array(); + protected $settings = array( + 'folders'=> '', + 'additionalEquals'=> '', + 'sorting'=> '', + 'direction'=> '', + 'categories'=> '', + + ); + protected array $allowedColumns = [ + 'crdate', + 'tstamp', + 'title', + 'uid', + ]; + + + public function initializeAction(): void + { + $this->galleryRepository = GeneralUtility::makeInstance(ImageGalleryRepository::class); + $this->orderFactory = GeneralUtility::makeInstance(OrderFactory::class); + $this->matcherFactory = GeneralUtility::makeInstance(MatcherFactory::class); + } + /** * @return void|string @@ -41,15 +67,21 @@ public function listAction() return 'Please save your plugin settings in the BE beforehand.'; } + $this->settings['folders'] = ConfigurationUtility::getInstance()->get('folders'); + $this->settings['additionalEquals'] = ConfigurationUtility::getInstance()->get('additionalEquals'); + $this->settings['sorting'] = ConfigurationUtility::getInstance()->get('sorting'); + $this->settings['direction'] = ConfigurationUtility::getInstance()->get('direction'); + $this->settings['categories'] = ConfigurationUtility::getInstance()->get('categories'); + // Initialize some objects related to the query. - $matcher = MatcherFactory::getInstance()->getMatcher($this->settings); - $order = OrderFactory::getInstance()->getOrder($this->settings); + $matcher = $this->matcherFactory->getMatcher($this->settings); +// $order = $this->orderFactory->getOrder($this->settings); - // Fetch the adequate repository for a known data type. - $contentRepository = ImageGalleryRepository::getInstance('sys_file'); + // Fetch and count files + $images = $this->galleryRepository->findByDemand($matcher, $this->getOrderings()); - // Fetch and count files - $images = $contentRepository->findBy($matcher, $order); + var_dump($this->settings); + exit(); // Assign template variables $this->view->assign('settings', $this->settings); @@ -57,15 +89,35 @@ public function listAction() $this->view->assign('images', $images); $identifiers = GeneralUtility::trimExplode(',', $this->settings['categories'], TRUE); - $this->view->assign('categories', $this->categoryRepository->findByIdentifiers($identifiers)); + $this->view->assign('categories', $this->galleryRepository->findByCategories($identifiers)); } - /** - * @param CategoryRepository $categoryRepository - */ - public function injectCategoryRepository(CategoryRepository $categoryRepository): void + protected function getOrderings(): array { - $this->categoryRepository = $categoryRepository; + $sortBy = $this->settings['sorting'] ?? 'crdate'; + if (!in_array($sortBy, $this->allowedColumns)) { + $sortBy = 'crdate'; + } + $defaultDirection = QueryInterface::ORDER_DESCENDING; + $direction = $this->settings['direction'] ?? $defaultDirection; + if ($this->settings['direction'] && strtoupper($direction) === 'DESC') { + $defaultDirection = QueryInterface::ORDER_ASCENDING; + } + return [ + $sortBy => $defaultDirection, + ]; } +// protected function getDemand(): array +// { +// $searchTerm = $this->request->hasArgument('searchTerm') ? $this->request->getArgument('searchTerm') : ''; +// $demand = []; +// if (strlen($searchTerm) > 0) { +// foreach ($this->demandFields as $field) { +// $demand[$field] = $searchTerm; +// } +// } +// return $demand; +// } + } diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index f02f501..c02ba76 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -2,14 +2,21 @@ namespace Fab\NaturalGallery\Domain\Repository; +use Fab\NaturalGallery\Persistence\Matcher; +use Fab\NaturalGallery\Persistence\Order; use Fab\NaturalGallery\Utility\ConfigurationUtility; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Utility\GeneralUtility; - -class ImageGalleryRepository +class ImageGalleryRepository { - public function getDefaultData(string $field):string + protected string $tableName = 'sys_file'; + protected array $settings; + + public function getDefaultData(string $field):string { return ConfigurationUtility::getInstance()->get($field); } @@ -31,11 +38,65 @@ public function findByUid(int $uid): array return is_array($messages) ? $messages : []; } - /** - * @throws Exception - * @throws DBALException - */ - public function findByDemand(array $demand = [], array $orderings = [], int $offset = 0, int $limit = 0): array + + public function findByCategory(int $category): array + { + /** @var QueryBuilder $query */ + $queryBuilder = $this->getQueryBuilder(); + $queryBuilder->select('*') + ->from($this->tableName) + ->innerJoin( + 'sys_file', + 'sys_file_metadata', + 'sys_file_metadata', + 'sys_file.uid = sys_file_metadata.file' + ) + ->innerJoin( + 'sys_file_metadata', + 'sys_category_record_mm', + 'sys_category_record_mm', + 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' + ) + ->where( + $queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $category) + ) + ->addOrderBy('sys_file_metadata.year', 'DESC') + ->addOrderBy('sys_file_metadata.title', 'ASC'); + + return $queryBuilder + ->execute() + ->fetchAllAssociative(); + } + public function findByCategories(array $categories): array + { + $queryBuilder = $this->getQueryBuilder(); + $queryBuilder->select('*') + ->from($this->tableName) + ->innerJoin( + 'sys_file', + 'sys_file_metadata', + 'sys_file_metadata', + 'sys_file.uid = sys_file_metadata.file' + ) + ->innerJoin( + 'sys_file_metadata', + 'sys_category_record_mm', + 'sys_category_record_mm', + 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' + ) + ->where( + $queryBuilder->expr()->in('sys_category_record_mm.uid_local', $categories) + ) + ->addOrderBy('sys_file_metadata.year', 'DESC') + ->addOrderBy('sys_file_metadata.title', 'ASC'); + + return $queryBuilder + ->execute() + ->fetchAllAssociative(); + } + + + public function findByDemand(array|Matcher $demand = [], array $orderings = [], int $offset = 0, int $limit = 0): array { $queryBuilder = $this->getQueryBuilder(); $queryBuilder->select('*')->from($this->tableName); @@ -63,10 +124,7 @@ public function findByDemand(array $demand = [], array $orderings = [], int $off return $queryBuilder->execute()->fetchAllAssociative(); } - /** - * @throws DBALException - * @throws Exception - */ + public function findByUids(array $uids): array { $query = $this->getQueryBuilder(); @@ -78,4 +136,21 @@ public function findByUids(array $uids): array return $query->execute()->fetchAllAssociative(); } -} \ No newline at end of file + protected function getQueryBuilder(): QueryBuilder + { + /** @var ConnectionPool $connectionPool */ + $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); + return $connectionPool->getQueryBuilderForTable($this->tableName); + } + + + + + + + + + + + +} diff --git a/Classes/Persistence/Matcher.php b/Classes/Persistence/Matcher.php new file mode 100644 index 0000000..6cb3e54 --- /dev/null +++ b/Classes/Persistence/Matcher.php @@ -0,0 +1,465 @@ + 'equals', + '!=' => 'notEquals', + 'in' => 'in', + 'like' => 'like', + '>' => 'greaterThan', + '>=' => 'greaterThanOrEqual', + '<' => 'lessThan', + '<=' => 'lessThanOrEqual', + ]; + + + protected array $equals = []; + + + protected array $notEquals = []; + + + protected array $greaterThan = []; + + + protected array $greaterThanOrEqual = []; + + + protected array $lessThan = []; + + + protected array $lessThanOrEqual = []; + + + protected array $in = []; + + + protected array $like = []; + + + protected array $matches = []; + + + protected string $defaultLogicalSeparator = self::LOGICAL_AND; + + + protected string $logicalSeparatorForEquals = self::LOGICAL_AND; + + + protected string $logicalSeparatorForGreaterThan = self::LOGICAL_AND; + + + protected string $logicalSeparatorForGreaterThanOrEqual = self::LOGICAL_AND; + + + protected string $logicalSeparatorForLessThan = self::LOGICAL_AND; + + + protected string $logicalSeparatorForLessThanOrEqual = self::LOGICAL_AND; + + + protected string $logicalSeparatorForIn = self::LOGICAL_AND; + + + protected string $logicalSeparatorForLike = self::LOGICAL_AND; + + + protected string $logicalSeparatorForSearchTerm = self::LOGICAL_OR; + + /** + * Constructs a new Matcher + * + * @param array $matches associative [$field => $value] + * @param string $dataType which corresponds to an entry of the TCA (table name). + */ + public function __construct(array $matches = [], string $dataType = '') + { + $this->dataType = $dataType; + $this->matches = $matches; + } + + /** + * @param string $searchTerm + * @return \Fab\Vidi\Persistence\Matcher + */ + public function setSearchTerm($searchTerm): Matcher + { + $this->searchTerm = $searchTerm; + return $this; + } + + /** + * @return string + */ + public function getSearchTerm(): string + { + return $this->searchTerm; + } + + /** + * @return array + */ + public function getEquals(): array + { + return $this->equals; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function equals($fieldNameAndPath, $operand): self + { + $this->equals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getNotEquals(): array + { + return $this->notEquals; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function notEquals($fieldNameAndPath, $operand): self + { + $this->notEquals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getGreaterThan(): array + { + return $this->greaterThan; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function greaterThan($fieldNameAndPath, $operand): self + { + $this->greaterThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getGreaterThanOrEqual(): array + { + return $this->greaterThanOrEqual; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function greaterThanOrEqual($fieldNameAndPath, $operand): self + { + $this->greaterThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getLessThan(): array + { + return $this->lessThan; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function lessThan($fieldNameAndPath, $operand): self + { + $this->lessThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getLessThanOrEqual(): array + { + return $this->lessThanOrEqual; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function lessThanOrEqual($fieldNameAndPath, $operand): self + { + $this->lessThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getLike(): array + { + return $this->like; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @return $this + */ + public function in($fieldNameAndPath, $operand): self + { + $this->in[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand]; + return $this; + } + + /** + * @return array + */ + public function getIn(): array + { + return $this->in; + } + + /** + * @param $fieldNameAndPath + * @param $operand + * @param bool $addWildCard + * @return $this + */ + public function like($fieldNameAndPath, $operand, bool $addWildCard = true): self + { + $wildCardSymbol = $addWildCard ? '%' : ''; + $this->like[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $wildCardSymbol . $operand . $wildCardSymbol]; + return $this; + } + + /** + * @return string + */ + public function getDefaultLogicalSeparator(): string + { + return $this->defaultLogicalSeparator; + } + + /** + * @param string $defaultLogicalSeparator + * @return $this + */ + public function setDefaultLogicalSeparator(string $defaultLogicalSeparator): self + { + $this->defaultLogicalSeparator = $defaultLogicalSeparator; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForEquals(): string + { + return $this->logicalSeparatorForEquals; + } + + /** + * @param string $logicalSeparatorForEquals + * @return $this + */ + public function setLogicalSeparatorForEquals(string $logicalSeparatorForEquals): self + { + $this->logicalSeparatorForEquals = $logicalSeparatorForEquals; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForGreaterThan(): string + { + return $this->logicalSeparatorForGreaterThan; + } + + /** + * @param string $logicalSeparatorForGreaterThan + * @return $this + */ + public function setLogicalSeparatorForGreaterThan(string $logicalSeparatorForGreaterThan): self + { + $this->logicalSeparatorForGreaterThan = $logicalSeparatorForGreaterThan; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForGreaterThanOrEqual(): string + { + return $this->logicalSeparatorForGreaterThanOrEqual; + } + + /** + * @param string $logicalSeparatorForGreaterThanOrEqual + * @return $this + */ + public function setLogicalSeparatorForGreaterThanOrEqual(string $logicalSeparatorForGreaterThanOrEqual): self + { + $this->logicalSeparatorForGreaterThanOrEqual = $logicalSeparatorForGreaterThanOrEqual; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForLessThan(): string + { + return $this->logicalSeparatorForLessThan; + } + + /** + * @param string $logicalSeparatorForLessThan + * @return $this + */ + public function setLogicalSeparatorForLessThan(string $logicalSeparatorForLessThan): self + { + $this->logicalSeparatorForLessThan = $logicalSeparatorForLessThan; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForLessThanOrEqual(): string + { + return $this->logicalSeparatorForLessThanOrEqual; + } + + /** + * @param string $logicalSeparatorForLessThanOrEqual + * @return $this + */ + public function setLogicalSeparatorForLessThanOrEqual(string $logicalSeparatorForLessThanOrEqual): self + { + $this->logicalSeparatorForLessThanOrEqual = $logicalSeparatorForLessThanOrEqual; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForIn(): string + { + return $this->logicalSeparatorForIn; + } + + /** + * @param string $logicalSeparatorForIn + * @return $this + */ + public function setLogicalSeparatorForIn(string $logicalSeparatorForIn): self + { + $this->logicalSeparatorForIn = $logicalSeparatorForIn; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForLike(): string + { + return $this->logicalSeparatorForLike; + } + + /** + * @param string $logicalSeparatorForLike + * @return $this + */ + public function setLogicalSeparatorForLike(string $logicalSeparatorForLike): self + { + $this->logicalSeparatorForLike = $logicalSeparatorForLike; + return $this; + } + + /** + * @return string + */ + public function getLogicalSeparatorForSearchTerm(): string + { + return $this->logicalSeparatorForSearchTerm; + } + + /** + * @param string $logicalSeparatorForSearchTerm + * @return $this + */ + public function setLogicalSeparatorForSearchTerm(string $logicalSeparatorForSearchTerm): self + { + $this->logicalSeparatorForSearchTerm = $logicalSeparatorForSearchTerm; + return $this; + } + + /** + * @return array + */ + public function getSupportedOperators(): array + { + return $this->supportedOperators; + } + + /** + * @return string + */ + public function getDataType(): string + { + return $this->dataType; + } + + /** + * @param string $dataType + * @return $this + */ + public function setDataType(string $dataType): self + { + $this->dataType = $dataType; + return $this; + } +} diff --git a/Classes/Persistence/MatcherFactory.php b/Classes/Persistence/MatcherFactory.php index 64e7c05..aaa9607 100644 --- a/Classes/Persistence/MatcherFactory.php +++ b/Classes/Persistence/MatcherFactory.php @@ -14,18 +14,13 @@ * The TYPO3 project - inspiring people to share! */ -use Fab\Vidi\Domain\Model\Selection; -use Fab\Vidi\Domain\Repository\SelectionRepository; -use Fab\Vidi\Resolver\FieldPathResolver; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\MathUtility; -use Fab\Vidi\Persistence\Matcher; -use Fab\Vidi\Tca\Tca; -use TYPO3\CMS\Extbase\Object\ObjectManager; -use TYPO3\CMS\Extbase\SignalSlot\Dispatcher; + /** * Factory class related to Matcher object. @@ -41,60 +36,26 @@ class MatcherFactory implements SingletonInterface /** * @var array */ - protected $dataType = 'sys_file'; + protected $tableName = 'sys_file'; - /** - * Gets a singleton instance of this class. - * - * @return MatcherFactory|object - */ - static public function getInstance() - { - return GeneralUtility::makeInstance(self::class); - } - /** - * Returns a matcher object. - * - * @param array $settings - * @return Matcher - */ - public function getMatcher(array $settings) + protected function getQueryBuilder(): QueryBuilder { - - $this->settings = $settings; - - /** @var $matcher Matcher */ - $matcher = GeneralUtility::makeInstance(Matcher::class); - - // We only want files of type images, consider it as a prerequisite. - $matcher->equals('type', File::FILETYPE_IMAGE); - - $matcher = $this->applyCriteriaFromFolders($matcher); - $matcher = $this->applyCriteriaFromSelection($matcher); - $matcher = $this->applyCriteriaFromAdditionalConstraints($matcher); - - // Trigger signal for post processing Matcher Object. - $this->emitPostProcessMatcherObjectSignal($matcher); - - return $matcher; + /** @var ConnectionPool $connectionPool */ + $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); + return $connectionPool->getQueryBuilderForTable($this->tableName); } - /** - * Apply criteria specific from folder given as settings. - * - * @param Matcher $matcher - * @return Matcher $matcher - */ - protected function applyCriteriaFromFolders(Matcher $matcher) + protected function applyCriteriaFromFolders(\Fab\NaturalGallery\Persistence\Matcher $matcher): \Fab\NaturalGallery\Persistence\Matcher { + $folders = ''; if (!empty($this->settings['folders'])) { - if (strpos($this->settings['folders'], 't3://') !== false) { + if (str_contains($this->settings['folders'], 't3://')) { $decodedUrl = urldecode($this->settings['folders']); // In case identifier=/ is missing. - if (strpos($decodedUrl, 'identifier=') === false) { + if (!str_contains($decodedUrl, 'identifier=')) { $decodedUrl .= '&identifier=/'; } preg_match("/storage=([\d]+)&identifier=(.+)/", $decodedUrl, $matches); @@ -120,14 +81,7 @@ protected function applyCriteriaFromFolders(Matcher $matcher) return $matcher; } - - /** - * Apply criteria from categories. - * - * @param Matcher $matcher - * @return Matcher $matcher - */ - protected function applyCriteriaFromAdditionalConstraints(Matcher $matcher) + protected function applyCriteriaFromAdditionalConstraints(Matcher $matcher): Matcher { if (!empty($this->settings['additionalEquals'])) { @@ -151,118 +105,15 @@ protected function applyCriteriaFromAdditionalConstraints(Matcher $matcher) return $matcher; } - /** - * Apply criteria from selection. - * - * @param Matcher $matcher - * @return Matcher $matcher - */ - protected function applyCriteriaFromSelection(Matcher $matcher) - { - - $selectionIdentifier = (int)$this->settings['selection']; - if ($selectionIdentifier > 0) { - - /** @var SelectionRepository $selectionRepository */ - $selectionRepository = $this->getObjectManager()->get(SelectionRepository::class); - - /** @var Selection $selection */ - $selection = $selectionRepository->findByUid($selectionIdentifier); - $queryParts = json_decode($selection->getQuery(), TRUE); - $matcher = $this->parseQuery($queryParts, $matcher, $this->dataType); - } - return $matcher; - } - - /** - * Apply criteria specific to jQuery plugin DataTable. - * - * @param array $queryParts - * @param Matcher $matcher - * @param string $dataType - * @return Matcher $matcher - */ - protected function parseQuery(array $queryParts, Matcher $matcher, $dataType) - { - - foreach ($queryParts as $queryPart) { - $fieldNameAndPath = key($queryPart); - - $resolvedDataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $dataType); - $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $dataType); - - // Retrieve the value. - $value = current($queryPart); - - if (Tca::grid($resolvedDataType)->hasFacet($fieldName) && Tca::grid($resolvedDataType)->facet($fieldName)->canModifyMatcher()) { - $matcher = Tca::grid($resolvedDataType)->facet($fieldName)->modifyMatcher($matcher, $value); - } elseif (Tca::table($resolvedDataType)->hasField($fieldName)) { - // Check whether the field exists and set it as "equal" or "like". - if ($this->isOperatorEquals($fieldNameAndPath, $dataType, $value)) { - $matcher->equals($fieldNameAndPath, $value); - } else { - $matcher->like($fieldNameAndPath, $value); - } - } elseif ($fieldNameAndPath === 'text') { - // Special case if field is "text" which is a pseudo field in this case. - // Set the search term which means Vidi will - // search in various fields with operator "like". The fields come from key "searchFields" in the TCA. - $matcher->setSearchTerm($value); - } - } - - return $matcher; - } - - /** - * Tell whether the operator should be equals instead of like for a search, e.g. if the value is numerical. - * - * @param string $fieldName - * @param string $dataType - * @param string $value - * @return bool - */ - protected function isOperatorEquals($fieldName, $dataType, $value) + public function getMatcher(array $settings): Matcher { - return (Tca::table($dataType)->field($fieldName)->hasRelation() && MathUtility::canBeInterpretedAsInteger($value)) - || Tca::table($dataType)->field($fieldName)->isNumerical(); - } - - /** - * Signal that is called for post-processing a matcher object. - * - * @param Matcher $matcher - * @signal - */ - protected function emitPostProcessMatcherObjectSignal(Matcher $matcher) - { - $this->getSignalSlotDispatcher()->dispatch(self::class, 'postProcessMatcherObject', array($matcher, $matcher->getDataType())); - } - /** - * Get the SignalSlot dispatcher - * - * @return Dispatcher - */ - protected function getSignalSlotDispatcher() - { - return $this->getObjectManager()->get(Dispatcher::class); - } - - /** - * @return ObjectManager - */ - protected function getObjectManager() - { - return GeneralUtility::makeInstance(ObjectManager::class); - } - - /** - * @return FieldPathResolver - */ - protected function getFieldPathResolver() - { - return GeneralUtility::makeInstance(FieldPathResolver::class); + $this->settings = $settings; + $matcher = GeneralUtility::makeInstance(Matcher::class); + // We only want files of type images, consider it as a prerequisite. + $matcher->equals('type', File::FILETYPE_IMAGE); + $matcher = $this->applyCriteriaFromFolders($matcher); + return $this->applyCriteriaFromAdditionalConstraints($matcher); } } diff --git a/Classes/Persistence/OrderFactory.php b/Classes/Persistence/OrderFactory.php index 2c58278..be4d2e6 100644 --- a/Classes/Persistence/OrderFactory.php +++ b/Classes/Persistence/OrderFactory.php @@ -14,8 +14,7 @@ * The TYPO3 project - inspiring people to share! */ -use Fab\Vidi\Persistence\Order; -use Fab\Vidi\Tca\Tca; +use Fab\Messenger\Utility\TcaFieldsUtility; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -25,22 +24,17 @@ class OrderFactory implements SingletonInterface { - /** - * @var array - */ - protected $settings = array(); - /** - * @var array - */ - protected $dataType = 'sys_file'; + protected array $settings = array(); + + protected string|array $dataType = 'sys_file'; /** * Gets a singleton instance of this class. * * @return OrderFactory */ - static public function getInstance() + static public function getInstance(): OrderFactory { return GeneralUtility::makeInstance(self::class); } @@ -51,12 +45,12 @@ static public function getInstance() * @param array $settings * @return Order */ - public function getOrder(array $settings = []) + public function getOrder(array $settings = []): Order { $this->settings = $settings; // Default ordering - $order = Tca::table($this->dataType)->getDefaultOrderings(); + $order = TcaFieldsUtility::getFields($this->dataType); if (!empty($this->settings['sorting'])) { $direction = empty($this->settings['direction']) ? 'ASC' : $this->settings['direction']; diff --git a/Classes/Persistence/order.php b/Classes/Persistence/order.php new file mode 100644 index 0000000..7820851 --- /dev/null +++ b/Classes/Persistence/order.php @@ -0,0 +1,54 @@ + $direction) { + $this->addOrdering($order, $direction); + } + } + + /** + * Add ordering + * + * @param string $order The order + * @param string $direction ASC / DESC + * @return void + */ + public function addOrdering(string $order, string $direction): void + { + $this->orderings[$order] = $direction; + } + + /** + * Returns the order + * + * @return array The order + */ + public function getOrderings(): array + { + return $this->orderings; + } +} diff --git a/Classes/Utility/TcaFieldsUtility.php b/Classes/Utility/TcaFieldsUtility.php new file mode 100644 index 0000000..9915063 --- /dev/null +++ b/Classes/Utility/TcaFieldsUtility.php @@ -0,0 +1,96 @@ +isAdmin()) { + foreach ($fields as $fieldName => $field) { + if (!self::hasAccess($fieldName)) { + unset($fields[$fieldName]); + } + } + } + return $fields; + } + + protected static function getBackendUser(): BackendUserAuthentication + { + return $GLOBALS['BE_USER']; + } + + protected static function hasAccess(string $fieldName): bool + { + $hasAccess = true; + if ( + self::hasTableAccess() && + isset($GLOBALS['TCA'][self::$tableName]['columns'][$fieldName]['exclude']) && + $GLOBALS['TCA'][self::$tableName]['columns'][$fieldName]['exclude'] + ) { + $hasAccess = self::getBackendUser()->check('non_exclude_fields', self::$tableName . ':' . $fieldName); + } + return $hasAccess; + } + + protected static function hasTableAccess(): bool + { + return self::getBackendUser()->check('tables_modify', self::$tableName); + } + + /** + * Remove fields according to Grid configuration. + * + * @param $fields + * @return array + */ + protected static function filterByExcludedFields($fields): array + { + // Unset excluded fields. + foreach (self::getExcludedFields() as $excludedField) { + if (isset($fields[$excludedField])) { + unset($fields[$excludedField]); + } + } + + return $fields; + } + + private static function getExcludedFields(): array + { + return empty($GLOBALS['TCA'][self::$tableName]['excluded_fields']) + ? [] + : GeneralUtility::trimExplode(',', $GLOBALS['TCA'][self::$tableName]['excluded_fields'], true); + } + + protected static function getIncludedFields(): array + { + return empty($GLOBALS['TCA'][self::$tableName]['included_fields']) + ? [] + : GeneralUtility::trimExplode(',', $GLOBALS['TCA'][self::$tableName]['included_fields'], true); + } +} diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 1c89005..862783c 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -1,14 +1,15 @@ -# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. -stockage_directory = +# cat=directory//; type=string; label=Storage Directory: Define the directory where files should be stored. +folders = -# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. -constraint = +# cat=directory//; type=string; label=Constraint: Define any specific constraints for file storage or access. +additionalEquals = -# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. -category = +# cat=directory//; type=string; label=Category: Define the category to which the items belong. +categories = -# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. -sorting = +# cat=directory//; type=string; label=Sorting: Specify how the items should be sorted by default. +sorting = + +# cat=directory//; type=string; label=Default Sorting Direction: Specify the default direction for sorting, e.g., ascending or descending. +direction = -# cat=directory//; type=string; label= Production redirect to: List of email addresses where to redirect all messages for "production" context. Notice, it can also be defined in $GLOBALS['TYPO3_CONF_VARS']['MAIL']['production_redirect_to'] = 'email@example.com' which will take the precedence if configured so.. -default_direction = \ No newline at end of file From d7980554953133d04fb928a629d2c82ced86a902 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Wed, 9 Oct 2024 11:34:40 +0000 Subject: [PATCH 5/9] fix: further adjustments to vidi files --- Classes/Controller/GalleryController.php | 7 +++---- .../Repository/ImageGalleryRepository.php | 13 +++++++++---- .../ViewHelpers/CategoryStackViewHelper.php | 4 ++-- Classes/ViewHelpers/ImageStackViewHelper.php | 19 ++++++++++++------- ext_conf_template.txt | 2 +- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index 156aaf7..3442645 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -80,8 +80,7 @@ public function listAction() // Fetch and count files $images = $this->galleryRepository->findByDemand($matcher, $this->getOrderings()); - var_dump($this->settings); - exit(); + // Assign template variables $this->view->assign('settings', $this->settings); @@ -94,9 +93,9 @@ public function listAction() protected function getOrderings(): array { - $sortBy = $this->settings['sorting'] ?? 'crdate'; + $sortBy = $this->settings['sorting'] ?? 'tstamp'; if (!in_array($sortBy, $this->allowedColumns)) { - $sortBy = 'crdate'; + $sortBy = 'tstamp'; } $defaultDirection = QueryInterface::ORDER_DESCENDING; $direction = $this->settings['direction'] ?? $defaultDirection; diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index c02ba76..cb1f080 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -83,16 +83,21 @@ public function findByCategories(array $categories): array 'sys_category_record_mm', 'sys_category_record_mm', 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' - ) - ->where( + ); + + if (!empty($categories)) { + $queryBuilder->where( $queryBuilder->expr()->in('sys_category_record_mm.uid_local', $categories) - ) - ->addOrderBy('sys_file_metadata.year', 'DESC') + ); + } + + $queryBuilder->addOrderBy('sys_file_metadata.year', 'DESC') ->addOrderBy('sys_file_metadata.title', 'ASC'); return $queryBuilder ->execute() ->fetchAllAssociative(); + } diff --git a/Classes/ViewHelpers/CategoryStackViewHelper.php b/Classes/ViewHelpers/CategoryStackViewHelper.php index 3295235..f6d2b8a 100644 --- a/Classes/ViewHelpers/CategoryStackViewHelper.php +++ b/Classes/ViewHelpers/CategoryStackViewHelper.php @@ -34,8 +34,8 @@ public function render() foreach ($cats as $cat) { $item = [ - 'id' => $cat->getUid(), - 'title' => $cat->getTitle() + 'id' => $cat['uid'], + 'title' => $cat['title'] ]; $items[] = $item; diff --git a/Classes/ViewHelpers/ImageStackViewHelper.php b/Classes/ViewHelpers/ImageStackViewHelper.php index 8cd2663..2a04958 100644 --- a/Classes/ViewHelpers/ImageStackViewHelper.php +++ b/Classes/ViewHelpers/ImageStackViewHelper.php @@ -35,19 +35,24 @@ public function render() $images = $this->templateVariableContainer->get('images'); $items = []; + foreach ($images as $image) { /** @var \TYPO3\CMS\Core\Resource\File $file */ - $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($image->getUid()); + $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($image['uid']); $thumbnailFile = $this->createProcessedFile($file, 'thumbnailMaximumWidth', 'thumbnailMaximumHeight'); $enlargedFile = $this->createProcessedFile($file, 'enlargedImageMaximumWidth', 'enlargedImageMaximumHeight'); - $categories = array_map(function ($cat) { - return [ - 'id' => $cat['uid'], - 'title' => $cat['title'] - ]; - }, $image['metadata']['categories']); + $categories = []; + + if (isset($image['metadata']['categories']) && is_array($image['metadata']['categories'])) { + $categories = array_map(function ($cat) { + return [ + 'id' => $cat['uid'], + 'title' => $cat['title'] + ]; + }, $image['metadata']['categories']); + } $baseUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); $item = [ diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 862783c..25bb3ce 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -5,7 +5,7 @@ folders = additionalEquals = # cat=directory//; type=string; label=Category: Define the category to which the items belong. -categories = +categories = 12,45,78,65,433 # cat=directory//; type=string; label=Sorting: Specify how the items should be sorted by default. sorting = From 9ae5e3ee87a91ab10d078bd7c654c68b36ad54eb Mon Sep 17 00:00:00 2001 From: Fabien Udriot Date: Mon, 21 Oct 2024 18:03:34 +0200 Subject: [PATCH 6/9] fixup! fix: further adjustments to vidi files --- Classes/Controller/GalleryController.php | 39 +++++++++---------- .../Repository/ImageGalleryRepository.php | 2 +- Classes/Persistence/MatcherFactory.php | 19 +-------- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index 3442645..ca8213a 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -1,4 +1,5 @@ '', - 'additionalEquals'=> '', - 'sorting'=> '', - 'direction'=> '', - 'categories'=> '', - + 'folders' => '', + 'additionalEquals' => '', + 'sorting' => '', + 'direction' => '', + 'categories' => '', ); + protected array $allowedColumns = [ 'crdate', 'tstamp', @@ -49,7 +51,6 @@ class GalleryController extends ActionController 'uid', ]; - public function initializeAction(): void { $this->galleryRepository = GeneralUtility::makeInstance(ImageGalleryRepository::class); @@ -57,7 +58,6 @@ public function initializeAction(): void $this->matcherFactory = GeneralUtility::makeInstance(MatcherFactory::class); } - /** * @return void|string */ @@ -67,20 +67,19 @@ public function listAction() return 'Please save your plugin settings in the BE beforehand.'; } - $this->settings['folders'] = ConfigurationUtility::getInstance()->get('folders'); - $this->settings['additionalEquals'] = ConfigurationUtility::getInstance()->get('additionalEquals'); - $this->settings['sorting'] = ConfigurationUtility::getInstance()->get('sorting'); - $this->settings['direction'] = ConfigurationUtility::getInstance()->get('direction'); - $this->settings['categories'] = ConfigurationUtility::getInstance()->get('categories'); +// $this->settings['folders'] = ConfigurationUtility::getInstance()->get('folders'); +// $this->settings['additionalEquals'] = ConfigurationUtility::getInstance()->get('additionalEquals'); +// $this->settings['sorting'] = ConfigurationUtility::getInstance()->get('sorting'); +// $this->settings['direction'] = ConfigurationUtility::getInstance()->get('direction'); +// $this->settings['categories'] = ConfigurationUtility::getInstance()->get('categories'); // Initialize some objects related to the query. - $matcher = $this->matcherFactory->getMatcher($this->settings); -// $order = $this->orderFactory->getOrder($this->settings); - - // Fetch and count files - $images = $this->galleryRepository->findByDemand($matcher, $this->getOrderings()); + $matcher = $this->matcherFactory->getMatcher($this->settings); - + // Fetch and count files + DebuggerUtility::var_dump($matcher); + exit(); + $images = $this->galleryRepository->findByDemand($matcher, $this->getOrderings()); // Assign template variables $this->view->assign('settings', $this->settings); @@ -110,7 +109,7 @@ protected function getOrderings(): array // protected function getDemand(): array // { // $searchTerm = $this->request->hasArgument('searchTerm') ? $this->request->getArgument('searchTerm') : ''; -// $demand = []; +// $matcher = []; // if (strlen($searchTerm) > 0) { // foreach ($this->demandFields as $field) { // $demand[$field] = $searchTerm; diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index cb1f080..8809c0d 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -67,6 +67,7 @@ public function findByCategory(int $category): array ->execute() ->fetchAllAssociative(); } + public function findByCategories(array $categories): array { $queryBuilder = $this->getQueryBuilder(); @@ -100,7 +101,6 @@ public function findByCategories(array $categories): array } - public function findByDemand(array|Matcher $demand = [], array $orderings = [], int $offset = 0, int $limit = 0): array { $queryBuilder = $this->getQueryBuilder(); diff --git a/Classes/Persistence/MatcherFactory.php b/Classes/Persistence/MatcherFactory.php index aaa9607..4f626bf 100644 --- a/Classes/Persistence/MatcherFactory.php +++ b/Classes/Persistence/MatcherFactory.php @@ -28,23 +28,9 @@ class MatcherFactory implements SingletonInterface { - /** - * @var array - */ - protected $settings = array(); + protected array $settings = []; - /** - * @var array - */ - protected $tableName = 'sys_file'; - - - protected function getQueryBuilder(): QueryBuilder - { - /** @var ConnectionPool $connectionPool */ - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - return $connectionPool->getQueryBuilderForTable($this->tableName); - } + protected string $tableName = 'sys_file'; protected function applyCriteriaFromFolders(\Fab\NaturalGallery\Persistence\Matcher $matcher): \Fab\NaturalGallery\Persistence\Matcher { @@ -107,7 +93,6 @@ protected function applyCriteriaFromAdditionalConstraints(Matcher $matcher): Mat public function getMatcher(array $settings): Matcher { - $this->settings = $settings; $matcher = GeneralUtility::makeInstance(Matcher::class); // We only want files of type images, consider it as a prerequisite. From 8e9d95728f2dd7be60273bfc3290d45423e85f65 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Tue, 22 Oct 2024 07:54:27 +0000 Subject: [PATCH 7/9] fix: reintegration of settings into flexform --- Classes/Persistence/Order.php | 54 ++++ Configuration/FlexForm/NaturalGallery.xml | 305 ++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 Classes/Persistence/Order.php diff --git a/Classes/Persistence/Order.php b/Classes/Persistence/Order.php new file mode 100644 index 0000000..7820851 --- /dev/null +++ b/Classes/Persistence/Order.php @@ -0,0 +1,54 @@ + $direction) { + $this->addOrdering($order, $direction); + } + } + + /** + * Add ordering + * + * @param string $order The order + * @param string $direction ASC / DESC + * @return void + */ + public function addOrdering(string $order, string $direction): void + { + $this->orderings[$order] = $direction; + } + + /** + * Returns the order + * + * @return array The order + */ + public function getOrderings(): array + { + return $this->orderings; + } +} diff --git a/Configuration/FlexForm/NaturalGallery.xml b/Configuration/FlexForm/NaturalGallery.xml index b89e0f1..add96ee 100644 --- a/Configuration/FlexForm/NaturalGallery.xml +++ b/Configuration/FlexForm/NaturalGallery.xml @@ -11,6 +11,83 @@ LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.source array + + + + + + input + 40 + trim + + + popup + link_popup.gif + + wizard_element_browser + + wizard + file + + + + page,url,mail,spec,file + + + height=900,width=900,status=0,menubar=0,scrollbars=1 + + + + + + + + + + input + + + + + + + + + input + + + + + + + + select + selectSingle + + + + + + + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.direction.descending + + DESC + + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.direction.ascending + + ASC + + + 1 + 1 + + + + @@ -31,6 +108,153 @@ LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.imageSettings array + + + + + + select + selectSingle + + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.thumbnailFormat.natural + + natural + + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.thumbnailFormat.square + + square + + + 0 + 1 + + + + + + + + check + 1 + + + + + + + + select + selectSingle + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.hover + hover + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.true + true + + + LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:settings.showLabels.false + false + + + 1 + 1 + + + + + + + + input + 4 + trim + + + + + + + + input + 0 + trim + + + + + + + + input + 1 + trim + + + + + + + + input + trim + + + + + + + + input + 350 + trim + + + + + + + + input + 1600 + trim + + + + + + + + input + trim + + + + @@ -41,6 +265,87 @@ LLL:EXT:natural_gallery/Resources/Private/Language/locallang.xlf:flexform.tab.filterBar array + + + + + + check + 0 + + + + + + + + check + 0 + + + + + + + + check + 0 + + + + + + + + check + 0 + + + + + + + + check + 0 + + + + + + + + select + selectMultipleSideBySide + 50 + sys_category + AND sys_category.sys_language_uid IN (-1, 0) ORDER BY sys_category.sorting ASC + 9999 + tree + 10 + + + 1 + 1 + + parent + + + + + From c2b38a6d81d60c0c23244bb7c3864dbc9f6e7266 Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Tue, 22 Oct 2024 18:02:31 +0000 Subject: [PATCH 8/9] fix: replace matcherfactory with demandfactory --- Classes/Controller/GalleryController.php | 41 +++-------- .../Repository/ImageGalleryRepository.php | 70 ++++++++----------- .../{MatcherFactory.php => DemandFactory.php} | 2 +- 3 files changed, 40 insertions(+), 73 deletions(-) rename Classes/Persistence/{MatcherFactory.php => DemandFactory.php} (98%) diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index ca8213a..0cdcaed 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -16,7 +16,7 @@ */ use Fab\NaturalGallery\Domain\Repository\ImageGalleryRepository; -use Fab\NaturalGallery\Persistence\MatcherFactory; +use Fab\NaturalGallery\Persistence\DemandFactory; use Fab\NaturalGallery\Persistence\OrderFactory; use Fab\NaturalGallery\Utility\ConfigurationUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -31,18 +31,13 @@ class GalleryController extends ActionController { protected ImageGalleryRepository $galleryRepository; - protected MatcherFactory $matcherFactory; + protected DemandFactory $demandFactory; + protected OrderFactory $orderFactory; protected array $configuration = array(); - protected $settings = array( - 'folders' => '', - 'additionalEquals' => '', - 'sorting' => '', - 'direction' => '', - 'categories' => '', - ); + protected $settings = array(); protected array $allowedColumns = [ 'crdate', @@ -52,10 +47,10 @@ class GalleryController extends ActionController ]; public function initializeAction(): void - { + { $this->galleryRepository = GeneralUtility::makeInstance(ImageGalleryRepository::class); $this->orderFactory = GeneralUtility::makeInstance(OrderFactory::class); - $this->matcherFactory = GeneralUtility::makeInstance(MatcherFactory::class); + $this->demandFactory = GeneralUtility::makeInstance(DemandFactory::class); } /** @@ -67,19 +62,10 @@ public function listAction() return 'Please save your plugin settings in the BE beforehand.'; } -// $this->settings['folders'] = ConfigurationUtility::getInstance()->get('folders'); -// $this->settings['additionalEquals'] = ConfigurationUtility::getInstance()->get('additionalEquals'); -// $this->settings['sorting'] = ConfigurationUtility::getInstance()->get('sorting'); -// $this->settings['direction'] = ConfigurationUtility::getInstance()->get('direction'); -// $this->settings['categories'] = ConfigurationUtility::getInstance()->get('categories'); - // Initialize some objects related to the query. - $matcher = $this->matcherFactory->getMatcher($this->settings); + $demand = $this->demandFactory->get($this->settings); - // Fetch and count files - DebuggerUtility::var_dump($matcher); - exit(); - $images = $this->galleryRepository->findByDemand($matcher, $this->getOrderings()); + $images = $this->galleryRepository->findByDemand($demand, $this->getOrderings()); // Assign template variables $this->view->assign('settings', $this->settings); @@ -106,16 +92,5 @@ protected function getOrderings(): array ]; } -// protected function getDemand(): array -// { -// $searchTerm = $this->request->hasArgument('searchTerm') ? $this->request->getArgument('searchTerm') : ''; -// $matcher = []; -// if (strlen($searchTerm) > 0) { -// foreach ($this->demandFields as $field) { -// $demand[$field] = $searchTerm; -// } -// } -// return $demand; -// } } diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index 8809c0d..d967d63 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -14,6 +14,7 @@ class ImageGalleryRepository { protected string $tableName = 'sys_file'; + protected array $settings; public function getDefaultData(string $field):string @@ -38,35 +39,34 @@ public function findByUid(int $uid): array return is_array($messages) ? $messages : []; } - - public function findByCategory(int $category): array - { - /** @var QueryBuilder $query */ - $queryBuilder = $this->getQueryBuilder(); - $queryBuilder->select('*') - ->from($this->tableName) - ->innerJoin( - 'sys_file', - 'sys_file_metadata', - 'sys_file_metadata', - 'sys_file.uid = sys_file_metadata.file' - ) - ->innerJoin( - 'sys_file_metadata', - 'sys_category_record_mm', - 'sys_category_record_mm', - 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' - ) - ->where( - $queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $category) - ) - ->addOrderBy('sys_file_metadata.year', 'DESC') - ->addOrderBy('sys_file_metadata.title', 'ASC'); - - return $queryBuilder - ->execute() - ->fetchAllAssociative(); - } + // public function findByCategory(int $category): array + // { + // /** @var QueryBuilder $query */ + // $queryBuilder = $this->getQueryBuilder(); + // $queryBuilder->select('*') + // ->from($this->tableName) + // ->innerJoin( + // 'sys_file', + // 'sys_file_metadata', + // 'sys_file_metadata', + // 'sys_file.uid = sys_file_metadata.file' + // ) + // ->innerJoin( + // 'sys_file_metadata', + // 'sys_category_record_mm', + // 'sys_category_record_mm', + // 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' + // ) + // ->where( + // $queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $category) + // ) + // ->addOrderBy('sys_file_metadata.year', 'DESC') + // ->addOrderBy('sys_file_metadata.title', 'ASC'); + + // return $queryBuilder + // ->execute() + // ->fetchAllAssociative(); + // } public function findByCategories(array $categories): array { @@ -115,6 +115,8 @@ public function findByDemand(array|Matcher $demand = [], array $orderings = [], $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards($value) . '%'), ); } + + if ($constraints) { $queryBuilder->where($queryBuilder->expr()->orX(...$constraints)); } @@ -148,14 +150,4 @@ protected function getQueryBuilder(): QueryBuilder return $connectionPool->getQueryBuilderForTable($this->tableName); } - - - - - - - - - - } diff --git a/Classes/Persistence/MatcherFactory.php b/Classes/Persistence/DemandFactory.php similarity index 98% rename from Classes/Persistence/MatcherFactory.php rename to Classes/Persistence/DemandFactory.php index 4f626bf..8a95135 100644 --- a/Classes/Persistence/MatcherFactory.php +++ b/Classes/Persistence/DemandFactory.php @@ -91,7 +91,7 @@ protected function applyCriteriaFromAdditionalConstraints(Matcher $matcher): Mat return $matcher; } - public function getMatcher(array $settings): Matcher + public function get(array $settings): Matcher { $this->settings = $settings; $matcher = GeneralUtility::makeInstance(Matcher::class); From 5b844a869dc35885bc60e0aa49d6f18e73e557fc Mon Sep 17 00:00:00 2001 From: Lebeau Valangui Date: Tue, 5 Nov 2024 17:10:01 +0000 Subject: [PATCH 9/9] fix: refactor galleryrepository --- Classes/Controller/GalleryController.php | 17 +++-- .../Repository/ImageGalleryRepository.php | 59 ++++++++++++---- Classes/Persistence/DemandFactory.php | 2 +- Classes/ViewHelpers/ImageStackViewHelper.php | 68 +++++++++++-------- 4 files changed, 95 insertions(+), 51 deletions(-) diff --git a/Classes/Controller/GalleryController.php b/Classes/Controller/GalleryController.php index 0cdcaed..c9049ae 100644 --- a/Classes/Controller/GalleryController.php +++ b/Classes/Controller/GalleryController.php @@ -62,10 +62,7 @@ public function listAction() return 'Please save your plugin settings in the BE beforehand.'; } - // Initialize some objects related to the query. - $demand = $this->demandFactory->get($this->settings); - - $images = $this->galleryRepository->findByDemand($demand, $this->getOrderings()); + $images = $this->galleryRepository->findByDemand($this->getDemand(), $this->getOrderings()); // Assign template variables $this->view->assign('settings', $this->settings); @@ -78,9 +75,9 @@ public function listAction() protected function getOrderings(): array { - $sortBy = $this->settings['sorting'] ?? 'tstamp'; + $sortBy = $this->settings['sorting'] ?? 'sys_file.tstamp'; if (!in_array($sortBy, $this->allowedColumns)) { - $sortBy = 'tstamp'; + $sortBy = 'sys_file.tstamp'; } $defaultDirection = QueryInterface::ORDER_DESCENDING; $direction = $this->settings['direction'] ?? $defaultDirection; @@ -92,5 +89,13 @@ protected function getOrderings(): array ]; } + protected function getDemand(): array + { + return [ + 'likes' => $this->demandFactory->get($this->settings), + 'identifiers' => GeneralUtility::trimExplode(',', $this->settings['categories'], TRUE) + ]; + } + } diff --git a/Classes/Domain/Repository/ImageGalleryRepository.php b/Classes/Domain/Repository/ImageGalleryRepository.php index d967d63..81de634 100644 --- a/Classes/Domain/Repository/ImageGalleryRepository.php +++ b/Classes/Domain/Repository/ImageGalleryRepository.php @@ -104,26 +104,55 @@ public function findByCategories(array $categories): array public function findByDemand(array|Matcher $demand = [], array $orderings = [], int $offset = 0, int $limit = 0): array { $queryBuilder = $this->getQueryBuilder(); - $queryBuilder->select('*')->from($this->tableName); - $constraints = []; - foreach ($demand as $field => $value) { - $constraints[] = $queryBuilder - ->expr() - ->like( - $field, - $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards($value) . '%'), - ); - } - - if ($constraints) { - $queryBuilder->where($queryBuilder->expr()->orX(...$constraints)); + + if ($demand['likes']) { + foreach ($demand['likes'] as $field => $value) { + $constraints[] = $queryBuilder->select('*')->from($this->tableName) + ->expr() + ->like( + $field, + $queryBuilder->createNamedParameter('%' . $queryBuilder->escapeLikeWildcards($value) . '%'), + ); + } + + if ($constraints) { + $queryBuilder->where($queryBuilder->expr()->orX(...$constraints)); + } + + # We handle the sorting + $queryBuilder->addOrderBy(key($orderings), current($orderings)); + } + + + if ($demand['identifiers']) { + $queryBuilder->select('*') + ->from($this->tableName) + ->innerJoin( + 'sys_file', + 'sys_file_metadata', + 'sys_file_metadata', + 'sys_file.uid = sys_file_metadata.file' + ) + ->innerJoin( + 'sys_file_metadata', + 'sys_category_record_mm', + 'sys_category_record_mm', + 'sys_category_record_mm.uid_foreign = sys_file_metadata.uid AND tablenames = "sys_file_metadata" AND fieldname = "categories"' + ); - # We handle the sorting - $queryBuilder->addOrderBy(key($orderings), current($orderings)); + if (!empty($demand['identifiers'])) { + $queryBuilder->where( + $queryBuilder->expr()->in('sys_category_record_mm.uid_local', $demand['identifiers']) + ); + } + $queryBuilder->addOrderBy('sys_file_metadata.year', 'DESC') + ->addOrderBy('sys_file_metadata.title', 'ASC'); + } + if ($limit > 0) { $queryBuilder->setMaxResults($limit); } diff --git a/Classes/Persistence/DemandFactory.php b/Classes/Persistence/DemandFactory.php index 8a95135..d0c7ca0 100644 --- a/Classes/Persistence/DemandFactory.php +++ b/Classes/Persistence/DemandFactory.php @@ -25,7 +25,7 @@ /** * Factory class related to Matcher object. */ -class MatcherFactory implements SingletonInterface +class DemandFactory implements SingletonInterface { protected array $settings = []; diff --git a/Classes/ViewHelpers/ImageStackViewHelper.php b/Classes/ViewHelpers/ImageStackViewHelper.php index 2a04958..8cc5c91 100644 --- a/Classes/ViewHelpers/ImageStackViewHelper.php +++ b/Classes/ViewHelpers/ImageStackViewHelper.php @@ -36,42 +36,52 @@ public function render() $items = []; - foreach ($images as $image) { + $processedUids = []; + $items = []; + foreach ($images as $image) { /** @var \TYPO3\CMS\Core\Resource\File $file */ - $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($image['uid']); - $thumbnailFile = $this->createProcessedFile($file, 'thumbnailMaximumWidth', 'thumbnailMaximumHeight'); - $enlargedFile = $this->createProcessedFile($file, 'enlargedImageMaximumWidth', 'enlargedImageMaximumHeight'); - - $categories = []; - - if (isset($image['metadata']['categories']) && is_array($image['metadata']['categories'])) { - $categories = array_map(function ($cat) { - return [ - 'id' => $cat['uid'], - 'title' => $cat['title'] + if (!empty($image['uid']) && !in_array($image['uid'], $processedUids)) { + try { + $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($image['uid']); + + $thumbnailFile = $this->createProcessedFile($file, 'thumbnailMaximumWidth', 'thumbnailMaximumHeight'); + $enlargedFile = $this->createProcessedFile($file, 'enlargedImageMaximumWidth', 'enlargedImageMaximumHeight'); + $categories = []; + + if (isset($image['metadata']['categories']) && is_array($image['metadata']['categories'])) { + $categories = array_map(function ($cat) { + return [ + 'id' => $cat['uid'], + 'title' => $cat['title'] + ]; + }, $image['metadata']['categories']); + } + + $baseUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); + $item = [ + 'thumbnail' => $baseUrl . $thumbnailFile->getPublicUrl(), + 'enlarged' => $baseUrl . $enlargedFile->getPublicUrl(), + 'id' => $file->getProperty('uid'), + 'title' => $file->getProperty('title'), + 'description' => $file->getProperty('description'), + 'tWidth' => $thumbnailFile->getProperty('width'), + 'tHeight' => $thumbnailFile->getProperty('height'), + 'eWidth' => $enlargedFile->getProperty('width'), + 'eHeight' => $enlargedFile->getProperty('height'), + 'categories' => $categories ]; - }, $image['metadata']['categories']); - } - $baseUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); - $item = [ - 'thumbnail' => $baseUrl . $thumbnailFile->getPublicUrl(), - 'enlarged' => $baseUrl . $enlargedFile->getPublicUrl(), - 'id' => $file->getProperty('uid'), - 'title' => $file->getProperty('title'), - 'description' => $file->getProperty('description'), - 'tWidth' => $thumbnailFile->getProperty('width'), - 'tHeight' => $thumbnailFile->getProperty('height'), - 'eWidth' => $enlargedFile->getProperty('width'), - 'eHeight' => $enlargedFile->getProperty('height'), - 'categories' => $categories - ]; - - $items[] = $item; + $items[] = $item; + $processedUids[] = $image['uid']; + } catch (\TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException $e) { + + } + } } return json_encode($items); + } /**