From dbf88efbcfb64812df0bd5c5118babf404ddf398 Mon Sep 17 00:00:00 2001 From: Daniel Gohlke Date: Sat, 8 Mar 2025 22:11:53 +0100 Subject: [PATCH] [TASK] Make extension compatible to TYPO3 v13.4 Relates: #87 --- .github/workflows/ci.yaml | 20 +- .gitlab-ci.yml | 25 +- Classes/Controller/EventController.php | 43 +--- Classes/Controller/EventDateController.php | 3 +- Classes/Domain/Model/AbstractEventDate.php | 13 +- Classes/Domain/Model/Cart/ProductFactory.php | 165 +++++++++++++ .../Model/Cart/ProductFactoryInterface.php | 17 ++ Classes/Domain/Model/Event.php | 9 +- Classes/Domain/Model/EventDate.php | 12 + Classes/Domain/Model/PriceCategory.php | 5 + .../Domain/Repository/EventDateRepository.php | 46 +--- Classes/Domain/Repository/EventRepository.php | 4 +- .../CheckProductAvailability.php | 8 +- .../RetrieveProductsFromRequest.php | 230 +++--------------- Classes/Exception/NotBookableException.php | 5 + .../ExtcodeCartEventsCTypeMigration.php | 41 ++++ Configuration/FlexForms/EventDatesPlugin.xml | 4 +- Configuration/FlexForms/ListEventsPlugin.xml | 28 +-- Configuration/FlexForms/SingleEventPlugin.xml | 1 - .../FlexForms/TeaserEventsPlugin.xml | 5 +- Configuration/TCA/Overrides/tt_content.php | 40 +-- .../tx_cartevents_domain_model_event.php | 3 +- .../TCA/tx_cartevents_domain_model_event.php | 94 +------ .../tx_cartevents_domain_model_eventdate.php | 94 +------ .../TSconfig/ContentElementWizard.tsconfig | 42 +--- Configuration/page.tsconfig | 3 + README.md | 26 +- Resources/Private/Language/locallang.xlf | 8 +- Resources/Public/JavaScripts/cart_events.js | 88 ++++--- Tests/Acceptance/AddEventDateToCartCest.php | 128 +++++++++- Tests/Acceptance/EventListCest.php | 104 +++++++- Tests/Acceptance/Support/Environment.php | 2 +- Tests/Fixtures/ContentDatabase.php | 6 +- Tests/Fixtures/EventsDatabase.php | 70 ++++++ .../Domain/Model/Cart/ProductFactoryTest.php | 214 ++++++++++++++++ .../Repository/EventDateRepositoryTest.php | 172 ++++++++++++- .../Domain/Repository/EventRepositoryTest.php | 2 +- composer.json | 18 +- ext_emconf.php | 4 +- ext_localconf.php | 22 +- rector.php | 8 +- shell.nix | 2 +- 42 files changed, 1156 insertions(+), 678 deletions(-) create mode 100644 Classes/Domain/Model/Cart/ProductFactory.php create mode 100644 Classes/Domain/Model/Cart/ProductFactoryInterface.php create mode 100644 Classes/Exception/NotBookableException.php create mode 100644 Classes/Updates/ExtcodeCartEventsCTypeMigration.php create mode 100644 Configuration/page.tsconfig create mode 100644 Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cfec80b7..8af1eac2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,7 +15,6 @@ jobs: strategy: matrix: php-version: - - 8.1 - 8.2 - 8.3 - 8.4 @@ -54,7 +53,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.1" + php-version: "8.2" tools: composer:v2 - name: Install dependencies @@ -70,14 +69,12 @@ jobs: strategy: matrix: include: - - php-version: '8.1' - typo3-version: '^12.4' - php-version: '8.2' - typo3-version: '^12.4' + typo3-version: '^13.4' - php-version: '8.3' - typo3-version: '^12.4' + typo3-version: '^13.4' - php-version: '8.4' - typo3-version: '^12.4' + typo3-version: '^13.4' steps: - uses: actions/checkout@v4 @@ -109,9 +106,6 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - - name: Run Unit Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-unit - - name: Run Unit Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-unit @@ -121,9 +115,6 @@ jobs: - name: Run Unit Tests PHP8.4 run: nix-shell --arg phpVersion \"php84\" --pure --run project-test-unit - - name: Run Functional Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-functional - - name: Run Functional Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-functional @@ -144,9 +135,6 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - - name: Run Acceptance Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-acceptance - - name: Run Acceptance Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-acceptance diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29082ec5..e4864d52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ cache: - .php_cs.cache variables: - TYPO3_VERSION: ^12.4 + TYPO3_VERSION: ^13.4 before_script: - apk add git --update @@ -39,11 +39,6 @@ lint:yaml: script: - find *.php Classes Configuration Tests -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l -lint:php81: - <<: *lint_php - variables: - CONTAINER_IMAGE: php:8.1-alpine - lint:php82: <<: *lint_php variables: @@ -60,11 +55,11 @@ lint:php84: CONTAINER_IMAGE: php:8.4-alpine phpstan:analyse: - image: $CI_REGISTRY/containers/phpunit-with-php-8.1:main + image: $CI_REGISTRY/containers/phpunit-with-php-8.2:main stage: lint before_script: - sed -i -e "s#ssh://git@code.extco.de:22722#https://gitlab-ci-token:$CI_JOB_TOKEN@code.extco.de#g" composer.json - - composer config platform.php 8.1 + - composer config platform.php 8.2 - composer install --no-progress --no-ansi --no-interaction script: - vendor/bin/codecept build @@ -83,12 +78,7 @@ phpstan:analyse: - composer require typo3/cms-core="${TYPO3_VERSION}" script: - vendor/bin/phpunit -c Build/UnitTests.xml - - typo3DatabaseDriver=pdo_sqlite vendor/bin/phpunit -c Build/FunctionalTests.xml - -test:php81: - <<: *test_php - variables: - CONTAINER_IMAGE: $CI_REGISTRY/containers/phpunit-with-php-8.1:main + - typo3DatabaseDriver=pdo_sqlite vendor/bin/phpunit -d memory_limit=256M -c Build/FunctionalTests.xml test:php82: <<: *test_php @@ -129,15 +119,8 @@ test:php84: expire_in: 1 day when: always -codeception:php81: - <<: *test_codeception - variables: - CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.1:main - codeception:php82: <<: *test_codeception - needs: - - codeception:php81 variables: CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.2:main diff --git a/Classes/Controller/EventController.php b/Classes/Controller/EventController.php index 8385330d..c2088152 100644 --- a/Classes/Controller/EventController.php +++ b/Classes/Controller/EventController.php @@ -28,9 +28,8 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Http\ForwardResponse; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface; -class EventController extends ActionController +final class EventController extends ActionController { private Cart $cart; @@ -118,6 +117,10 @@ public function showAction(?Event $event = null): ResponseInterface #[IgnoreValidation(['value' => 'priceCategory'])] public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCategory = null): ResponseInterface { + if (class_exists(\TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class) === false) { + throw new \BadFunctionCallException('This action requires the installation of typo3/cms-form.'); + } + if (!$eventDate) { $arguments = $this->request->getArguments(); foreach ($arguments as $argumentKey => $argumentValue) { @@ -133,7 +136,7 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa $formDefinition = $eventDate->getEvent()->getFormDefinition(); $formPersistenceManager = GeneralUtility::makeInstance( - FormPersistenceManagerInterface::class + \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class ); $form = $formPersistenceManager->load($formDefinition); @@ -195,28 +198,7 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa return $this->htmlResponse(); } - protected function getEvent(): ?Event - { - $eventUid = 0; - - if ((int)$GLOBALS['TSFE']->page['doktype'] == 186) { - $eventUid = (int)$GLOBALS['TSFE']->page['cart_events_event']; - } - - if ($eventUid > 0) { - $event = $this->eventRepository->findByUid($eventUid); - if ($event && $event instanceof Event) { - return $event; - } - } - - return null; - } - - /** - * Create the demand object which define which records will get shown - */ - protected function createDemandObjectFromSettings(string $type, array $settings): EventDemand + private function createDemandObjectFromSettings(string $type, array $settings): EventDemand { /** @var EventDemand $demand */ $demand = GeneralUtility::makeInstance( @@ -254,7 +236,7 @@ protected function createDemandObjectFromSettings(string $type, array $settings) return $demand; } - protected function addCategoriesToDemandObjectFromSettings(EventDemand &$demand): void + private function addCategoriesToDemandObjectFromSettings(EventDemand &$demand): void { if ($this->settings['categoriesList']) { $selectedCategories = GeneralUtility::intExplode( @@ -281,10 +263,7 @@ protected function addCategoriesToDemandObjectFromSettings(EventDemand &$demand) } } - /** - * assigns currency translation array to view - */ - protected function assignCurrencyTranslationData(): void + private function assignCurrencyTranslationData(): void { $this->restoreSession(); @@ -297,7 +276,7 @@ protected function assignCurrencyTranslationData(): void $this->view->assign('currencyTranslationData', $currencyTranslationData); } - protected function addCacheTags(iterable $events): void + private function addCacheTags(iterable $events): void { $cacheTags = []; @@ -333,7 +312,7 @@ private function forwardToShowActionWhenRequested(): ?ForwardResponse return $forwardResponse->withArguments(['event' => $this->request->getArgument('event')]); } - protected function restoreSession(): void + private function restoreSession(): void { $cart = $this->sessionHandler->restoreCart($this->cartConfiguration['settings']['cart']['pid']); diff --git a/Classes/Controller/EventDateController.php b/Classes/Controller/EventDateController.php index f307a562..0b43e027 100644 --- a/Classes/Controller/EventDateController.php +++ b/Classes/Controller/EventDateController.php @@ -10,12 +10,13 @@ * For the full copyright and license information, please read the * LICENSE file that was distributed with this source code. */ + use Extcode\CartEvents\Domain\Repository\EventDateRepository; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -class EventDateController extends ActionController +final class EventDateController extends ActionController { public function __construct( private readonly EventDateRepository $eventDateRepository, diff --git a/Classes/Domain/Model/AbstractEventDate.php b/Classes/Domain/Model/AbstractEventDate.php index 277852b3..68a338b3 100644 --- a/Classes/Domain/Model/AbstractEventDate.php +++ b/Classes/Domain/Model/AbstractEventDate.php @@ -11,32 +11,33 @@ * LICENSE file that was distributed with this source code. */ +use DateTime; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; abstract class AbstractEventDate extends AbstractEntity { - protected ?\DateTime $begin = null; + protected ?DateTime $begin = null; - protected ?\DateTime $end = null; + protected ?DateTime $end = null; protected string $note = ''; - public function getBegin(): ?\DateTime + public function getBegin(): ?DateTime { return $this->begin; } - public function setBegin(\DateTime $begin): void + public function setBegin(DateTime $begin): void { $this->begin = $begin; } - public function getEnd(): ?\DateTime + public function getEnd(): ?DateTime { return $this->end; } - public function setEnd(\DateTime $end): void + public function setEnd(DateTime $end): void { $this->end = $end; } diff --git a/Classes/Domain/Model/Cart/ProductFactory.php b/Classes/Domain/Model/Cart/ProductFactory.php new file mode 100644 index 00000000..0dcee888 --- /dev/null +++ b/Classes/Domain/Model/Cart/ProductFactory.php @@ -0,0 +1,165 @@ +getEventDateFromRequestArgument($requestArguments['eventDate']); + + $priceCategory = null; + if (isset($requestArguments['priceCategory'])) { + $priceCategory = $this->getPriceCategoryFromRequestArgument($requestArguments['priceCategory']); + } + + return $this->getProductFromEventDate( + $quantity, + $taxClasses, + $isNetPrice, + $eventDate, + $priceCategory, + ); + } + + private function getEventDateFromRequestArgument( + mixed $identifier, + ): EventDate { + if (is_numeric($identifier) === false) { + throw new InvalidArgumentException('Event date argument is invalid', 1741692831); + } + + $eventDate = $this->eventDateRepository->findByUid($identifier); + + if (($eventDate instanceof EventDate) === false) { + throw new InvalidArgumentException('Event date not found', 1741693220); + } + + if ($eventDate->isBookable() === false) { + throw new NotBookableException('Event date not bookable', 1741693273); + } + + return $eventDate; + } + + private function getPriceCategoryFromRequestArgument( + int $identifier, + ): PriceCategory { + if (is_numeric($identifier) === false) { + throw new InvalidArgumentException('Price category argument is invalid', 1741692831); + } + + $priceCategory = $this->priceCategoryRepository->findByUid($identifier); + + if (($priceCategory instanceof PriceCategory) === false) { + throw new InvalidArgumentException('Price category not found', 1741693444); + } + + if (!$priceCategory->isBookable()) { + throw new NotBookableException('Price category not bookable', 1741693482); + } + + return $priceCategory; + } + + private function getProductFromEventDate( + int $quantity, + array $taxClasses, + bool $isNetPrice, + EventDate $eventDate, + ?PriceCategory $priceCategory = null, + ): Product { + $event = $eventDate->getEvent(); + $title = implode(' - ', [$event->getTitle(), $eventDate->getTitle()]); + $sku = implode(' - ', [$event->getSku(), $eventDate->getSku()]); + + $price = $eventDate->getBestPrice(); + if ($priceCategory instanceof PriceCategory) { + $price = $priceCategory->getBestPrice(); + } + + $product = new Product( + 'CartEvents', + $eventDate->getUid(), + $sku, + $title, + $price, + $taxClasses[$event->getTaxClassId()], + $quantity, + $isNetPrice, + null + ); + $product->setIsVirtualProduct($event->isVirtualProduct()); + + if ($priceCategory instanceof PriceCategory) { + $product->addBeVariant($this->getProductBackendVariant($product, $quantity, $priceCategory)); + } + + return $product; + } + + private function getProductBackendVariant( + Product $product, + int $quantity, + PriceCategory $priceCategory, + ): BeVariant { + $cartBackendVariant = GeneralUtility::makeInstance( + BeVariant::class, + PriceCategory::class . '-' . $priceCategory->getUid(), + $product, + $priceCategory->getTitle(), + $priceCategory->getSku(), + 1, + $priceCategory->getBestPrice(), + $quantity + ); + + /* + TODO + if ($bestSpecialPrice) { + $cartBackendVariant->setSpecialPrice($bestSpecialPrice->getPrice()); + } + */ + + return $cartBackendVariant; + } +} diff --git a/Classes/Domain/Model/Cart/ProductFactoryInterface.php b/Classes/Domain/Model/Cart/ProductFactoryInterface.php new file mode 100644 index 00000000..481074c3 --- /dev/null +++ b/Classes/Domain/Model/Cart/ProductFactoryInterface.php @@ -0,0 +1,17 @@ +images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->eventDates = new ObjectStorage(); + $this->relatedEvents = new ObjectStorage(); + $this->relatedEventsFrom = new ObjectStorage(); + } public function isVirtualProduct(): bool { diff --git a/Classes/Domain/Model/EventDate.php b/Classes/Domain/Model/EventDate.php index 5bcacc1b..cf9a0ef5 100644 --- a/Classes/Domain/Model/EventDate.php +++ b/Classes/Domain/Model/EventDate.php @@ -51,6 +51,9 @@ class EventDate extends AbstractEventDate protected bool $priceCategorized = false; + /** + * @var ObjectStorage + */ #[Cascade(['value' => 'remove'])] protected ObjectStorage $priceCategories; @@ -68,6 +71,15 @@ class EventDate extends AbstractEventDate #[Cascade(['value' => 'remove'])] protected ObjectStorage $calendarEntries; + public function __construct() + { + $this->images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->specialPrices = new ObjectStorage(); + $this->priceCategories = new ObjectStorage(); + $this->calendarEntries = new ObjectStorage(); + } + public function getSku(): string { return $this->sku; diff --git a/Classes/Domain/Model/PriceCategory.php b/Classes/Domain/Model/PriceCategory.php index 11bdcdb4..10bd1138 100644 --- a/Classes/Domain/Model/PriceCategory.php +++ b/Classes/Domain/Model/PriceCategory.php @@ -37,6 +37,11 @@ class PriceCategory extends AbstractEntity protected int $seatsTaken = 0; + public function __construct() + { + $this->specialPrices = new ObjectStorage(); + } + public function getSku(): string { return $this->sku; diff --git a/Classes/Domain/Repository/EventDateRepository.php b/Classes/Domain/Repository/EventDateRepository.php index 098f7895..25bffc4b 100644 --- a/Classes/Domain/Repository/EventDateRepository.php +++ b/Classes/Domain/Repository/EventDateRepository.php @@ -10,17 +10,14 @@ * For the full copyright and license information, please read the * LICENSE file that was distributed with this source code. */ -use Doctrine\DBAL\Driver\Statement; + use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Repository; class EventDateRepository extends Repository { - /** - * @return Statement|int - */ - public function findNext(int $limit, string $pidList) + public function findNext(int $limit, string $pidList): array { $table = 'tx_cartevents_domain_model_eventdate'; $joinTableEvent = 'tx_cartevents_domain_model_event'; @@ -61,45 +58,10 @@ public function findNext(int $limit, string $pidList) $queryBuilder->setMaxResults($limit); } - return $queryBuilder->execute(); - } - - /** - * @param $queryBuilder - * @return mixed - */ - protected function joinCategory($queryBuilder) - { - return $queryBuilder - ->leftJoin( - 'event', - 'sys_category_record_mm', - 'categoryMM', - $queryBuilder->expr()->eq( - 'categoryMM.uid_foreign', - $queryBuilder->quoteIdentifier('event.uid') - ) - ) - ->where( - $queryBuilder->expr()->eq('categoryMM.tablenames', '"tx_cartevents_domain_model_event"'), - $queryBuilder->expr()->eq('categoryMM.fieldname', '"category"') - ) - ->leftJoin( - 'categoryMM', - 'sys_category', - 'category', - $queryBuilder->expr()->eq( - 'category.uid', - $queryBuilder->quoteIdentifier('categoryMM.uid_local') - ) - ); + return $queryBuilder->executeQuery()->fetchAllAssociative(); } - /** - * @param string $pidList - * @return array - */ - protected function getPids(string $pidList): array + private function getPids(string $pidList): array { return GeneralUtility::intExplode(',', $pidList, true); } diff --git a/Classes/Domain/Repository/EventRepository.php b/Classes/Domain/Repository/EventRepository.php index c47ad616..56df5562 100644 --- a/Classes/Domain/Repository/EventRepository.php +++ b/Classes/Domain/Repository/EventRepository.php @@ -78,7 +78,7 @@ public function findByUids(string $uids, ?int $limit = null): array return $this->orderByField($query->execute(), $uids); } - protected function createOrderingsFromDemand(EventDemand $demand): array + private function createOrderingsFromDemand(EventDemand $demand): array { $orderings = []; @@ -101,7 +101,7 @@ protected function createOrderingsFromDemand(EventDemand $demand): array return $orderings; } - protected function orderByField(QueryResultInterface $events, array $uids): array + private function orderByField(QueryResultInterface $events, array $uids): array { $indexedEvents = []; $orderedEvents = []; diff --git a/Classes/EventListener/CheckProductAvailability.php b/Classes/EventListener/CheckProductAvailability.php index 18492fe3..db527aa0 100644 --- a/Classes/EventListener/CheckProductAvailability.php +++ b/Classes/EventListener/CheckProductAvailability.php @@ -52,15 +52,17 @@ public function __invoke(CheckProductAvailabilityEvent $listenerEvent): void return; } - if (!$this->eventDate->isHandleSeatsInPriceCategory()) { + if ($this->eventDate->isHandleSeatsInPriceCategory() === false) { $this->hasEventDateEnoughSeats($cartProduct, $cart, $mode, (int)$quantity, $listenerEvent); return; } foreach ($this->eventDate->getPriceCategories() as $priceCategory) { $beVariantId = PriceCategory::class . '-' . $priceCategory->getUid(); - $quantity = (int)$quantity[$beVariantId]; - $this->hasPriceCategoryEnoughSeats($cartProduct, $cart, $mode, $beVariantId, $quantity, $priceCategory, $listenerEvent); + if (array_key_exists($beVariantId, $cartProduct->getBeVariants()) === false) { + continue; + } + $this->hasPriceCategoryEnoughSeats($cartProduct, $cart, $mode, $beVariantId, (int)$quantity, $priceCategory, $listenerEvent); } } diff --git a/Classes/EventListener/RetrieveProductsFromRequest.php b/Classes/EventListener/RetrieveProductsFromRequest.php index a2cc3147..6345be96 100644 --- a/Classes/EventListener/RetrieveProductsFromRequest.php +++ b/Classes/EventListener/RetrieveProductsFromRequest.php @@ -11,230 +11,62 @@ * LICENSE file that was distributed with this source code. */ -use Extcode\Cart\Domain\Model\Cart\BeVariant; -use Extcode\Cart\Domain\Model\Cart\Cart; +use Exception; use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\Cart\Event\RetrieveProductsFromRequestEvent; -use Extcode\CartEvents\Domain\Model\EventDate; -use Extcode\CartEvents\Domain\Model\PriceCategory; -use Extcode\CartEvents\Domain\Repository\EventDateRepository; -use Extcode\CartEvents\Domain\Repository\PriceCategoryRepository; +use Extcode\CartEvents\Domain\Model\Cart\ProductFactoryInterface; +use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; -use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; class RetrieveProductsFromRequest { - protected Cart $cart; - - protected EventDate $eventDate; - - protected ?PriceCategory $priceCategory = null; - public function __construct( - private readonly EventDateRepository $eventDateRepository, - private readonly PriceCategoryRepository $priceCategoryRepository + private readonly ExtensionConfiguration $extensionConfiguration, + private readonly ProductFactoryInterface $productFactory, ) {} public function __invoke(RetrieveProductsFromRequestEvent $event): void { - $request = $event->getRequest(); - $this->cart = $event->getCart(); - $requestArguments = $request->getArguments(); - $taxClasses = $this->cart->getTaxClasses(); + $requestArguments = $event->getRequest()->getArguments(); if ($requestArguments['productType'] !== 'CartEvents') { return; } - $errors = $this->checkRequestArguments($requestArguments); - - if (!empty($errors)) { - $event->setErrors($errors); - return; - } - - $quantity = (int)$requestArguments['quantity']; - - $this->eventDate = $this->eventDateRepository->findByUid((int)$requestArguments['eventDate']); + try { + $product = $this->productFactory->createProductFromRequestArguments( + $requestArguments, + $event->getCart()->getTaxClasses(), + (bool)$this->extensionConfiguration->get('cart_events', 'inputIsNetPrice'), + ); - if (!$this->eventDate) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.error.event_date_not_found', + if ($product instanceof Product) { + $event->addProduct($product); + } + } catch (ResourceNotFoundException $exception) { + $event->setErrors( + [ + 'messageBody' => LocalizationUtility::translate( + 'tx_cartevents.retrieve_product_from_request.error.' . $exception->getCode(), 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) + ) ?? $exception->getMessage(), + 'severity' => ContextualFeedbackSeverity::WARNING, + ] ); return; - } - - if (!$this->eventDate->isBookable()) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.error.event_is_not_bookable', + } catch (Exception $exception) { + $event->setErrors( + [ + 'messageBody' => LocalizationUtility::translate( + $exception->getCode(), 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) + ) ?? $exception->getMessage(), + 'severity' => ContextualFeedbackSeverity::ERROR, + ] ); return; } - - if (isset($requestArguments['priceCategory'])) { - if (!(int)$requestArguments['priceCategory']) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.invalid_event_date_category_price', - 'CartEvents' - ), - '', - ContextualFeedbackSeverity::ERROR - ) - ); - return; - } - - $this->priceCategory = $this->priceCategoryRepository->findByUid((int)$requestArguments['priceCategory']); - - if (!$this->priceCategory->isBookable()) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.event_date.price_category.is_not_bookable', - 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) - ); - return; - } - } - - $event->addProduct( - $this->getProductFromEventDate($quantity, $taxClasses) - ); - } - - /** - * @param int $quantity - * @param array $taxClasses - * - * @return Product - */ - protected function getProductFromEventDate( - int $quantity, - array $taxClasses - ) { - $event = $this->eventDate->getEvent(); - $title = implode(' - ', [$event->getTitle(), $this->eventDate->getTitle()]); - $sku = implode(' - ', [$event->getSku(), $this->eventDate->getSku()]); - - $price = $this->eventDate->getBestPrice(); - if ($this->priceCategory instanceof PriceCategory) { - $price = $this->priceCategory->getBestPrice(); - } - - $inputIsNetPrice = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('cart_events', 'inputIsNetPrice'); - - $product = new Product( - 'CartEvents', - $this->eventDate->getUid(), - $sku, - $title, - $price, - $taxClasses[$event->getTaxClassId()], - $quantity, - $inputIsNetPrice, - null - ); - $product->setIsVirtualProduct($event->isVirtualProduct()); - - if ($this->priceCategory instanceof PriceCategory) { - $product->addBeVariant($this->getProductBackendVariant($product, $quantity)); - } - - if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate'])) { - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate'] ?? [] as $className) { - $params = [ - 'cart' => $this->cart, - 'eventDate' => $this->eventDate, - ]; - - $_procObj = GeneralUtility::makeInstance($className); - $_procObj->changeProductFromEventDate($product, $params); - } - } - - return $product; - } - - /** - * @param Product $product - * @param int $quantity - * - * @return BeVariant - */ - protected function getProductBackendVariant( - Product $product, - int $quantity - ): BeVariant { - $cartBackendVariant = GeneralUtility::makeInstance( - BeVariant::class, - PriceCategory::class . '-' . $this->priceCategory->getUid(), - $product, - $this->priceCategory->getTitle(), - $this->priceCategory->getSku(), - 1, - $this->priceCategory->getBestPrice(), - $quantity - ); - - /* - TODO - if ($bestSpecialPrice) { - $cartBackendVariant->setSpecialPrice($bestSpecialPrice->getPrice()); - } - */ - - return $cartBackendVariant; - } - - protected function checkRequestArguments(array $requestArguments): array - { - if (!(int)$requestArguments['eventDate']) { - return [ - 'messageBody' => LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.invalid_event_date', - 'CartEvents' - ), - 'severity' => ContextualFeedbackSeverity::ERROR, - ]; - } - - if ((int)$requestArguments['quantity'] < 0) { - return [ - 'messageBody' => LocalizationUtility::translate( - 'tx_cart.error.invalid_quantity', - 'CartEvents' - ), - 'severity' => ContextualFeedbackSeverity::WARNING, - ]; - } - - return []; } } diff --git a/Classes/Exception/NotBookableException.php b/Classes/Exception/NotBookableException.php new file mode 100644 index 00000000..6ad69e46 --- /dev/null +++ b/Classes/Exception/NotBookableException.php @@ -0,0 +1,5 @@ + 'pi_plugin1', + * 'pi_plugin2' => 'new_content_element', + * ] + * + * @return array + */ + protected function getListTypeToCTypeMapping(): array + { + return [ + // TODO: Add this mapping yourself! + ]; + } +} diff --git a/Configuration/FlexForms/EventDatesPlugin.xml b/Configuration/FlexForms/EventDatesPlugin.xml index b2cf3b06..2667a740 100644 --- a/Configuration/FlexForms/EventDatesPlugin.xml +++ b/Configuration/FlexForms/EventDatesPlugin.xml @@ -21,7 +21,6 @@ group - db pages 1 1 @@ -39,10 +38,9 @@ 1 - input + number 3 3 - int 1 0 diff --git a/Configuration/FlexForms/ListEventsPlugin.xml b/Configuration/FlexForms/ListEventsPlugin.xml index a98d3dbf..5437017d 100644 --- a/Configuration/FlexForms/ListEventsPlugin.xml +++ b/Configuration/FlexForms/ListEventsPlugin.xml @@ -12,7 +12,7 @@ selectSingle Extcode\Cart\Hooks\ItemsProcFunc->user_templateLayout cart_events - Events + ListEvents @@ -24,25 +24,25 @@ select selectSingle - - - + + + - LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.events.action.event.list.orderBy.tstamp - tstamp + + tstamp\ - LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.events.action.event.list.orderBy.sorting - sorting + + sorting - LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.events.action.event.list.orderBy.crdate - crdate + + crdate - LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.events.action.event.list.orderBy.title - title + + title @@ -76,10 +76,9 @@ 1 - input + number 3 3 - int 1 0 @@ -94,7 +93,6 @@ group - db pages 1 1 diff --git a/Configuration/FlexForms/SingleEventPlugin.xml b/Configuration/FlexForms/SingleEventPlugin.xml index 9149748f..b5cc6d93 100644 --- a/Configuration/FlexForms/SingleEventPlugin.xml +++ b/Configuration/FlexForms/SingleEventPlugin.xml @@ -13,7 +13,6 @@ Extcode\Cart\Hooks\ItemsProcFunc->user_templateLayout cart_events SingleEvent - selectSingle diff --git a/Configuration/FlexForms/TeaserEventsPlugin.xml b/Configuration/FlexForms/TeaserEventsPlugin.xml index b71375c3..b50610fe 100644 --- a/Configuration/FlexForms/TeaserEventsPlugin.xml +++ b/Configuration/FlexForms/TeaserEventsPlugin.xml @@ -20,10 +20,9 @@ 1 - input + number 3 3 - int 1 0 @@ -38,7 +37,6 @@ group - db pages 1 1 @@ -58,7 +56,6 @@ select selectMultipleSideBySide - 1 tx_cartevents_domain_model_event 3 1 diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index bc417ba3..8abbb6f8 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -11,39 +11,51 @@ $pluginNames = [ 'ShowEvent' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.show_event', ], 'ListEvents' => [ - 'subtypes_excludelist' => 'select_key', + 'additionalNewFields' => 'pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-list', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.list_events', ], 'TeaserEvents' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-teaser', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.teaser_events', ], 'SingleEvent' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.single_event', ], 'EventDates' => [ - 'subtypes_excludelist' => 'select_key, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.event_dates', ], ]; foreach ($pluginNames as $pluginName => $pluginConf) { - $pluginSignature = 'cartevents_' . strtolower($pluginName); - $pluginNameSC = strtolower((string)preg_replace('/[A-Z]/', '_$0', lcfirst($pluginName))); - ExtensionUtility::registerPlugin( - 'CartEvents', + $pluginSignature = ExtensionUtility::registerPlugin( + 'cart_events', $pluginName, - $_LLL_be . 'tx_cartevents.plugin.' . $pluginNameSC . '.title' + $pluginConf['translationKeyPrefix'] . '.title', + $pluginConf['pluginIcon'], + 'cart', + $pluginConf['translationKeyPrefix'] . '.description', ); - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] = $pluginConf['subtypes_excludelist']; - $flexFormPath = 'EXT:cart_events/Configuration/FlexForms/' . $pluginName . 'Plugin.xml'; if (file_exists(GeneralUtility::getFileAbsFileName($flexFormPath))) { - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] = 'pi_flexform'; + ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + rtrim('--div--;Configuration,pi_flexform,' . ($pluginConf['additionalNewFields'] ?? ''), ','), + $pluginSignature, + 'after:subheader', + ); + ExtensionManagementUtility::addPiFlexFormValue( + '*', + 'FILE:' . $flexFormPath, $pluginSignature, - 'FILE:' . $flexFormPath ); } } diff --git a/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php b/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php index 8b5a5a34..17a0d6b3 100644 --- a/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php +++ b/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php @@ -2,6 +2,7 @@ defined('TYPO3') or die(); +use Extcode\Cart\Hooks\FormDefinitions; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -21,7 +22,7 @@ 'items' => [ ['label' => 'LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.selectPersistenceIdentifier', 'value' => ''], ], - 'itemsProcFunc' => 'Extcode\\Cart\\Hooks\\ItemsProcFunc->user_formDefinition', + 'itemsProcFunc' => FormDefinitions::class . '->getItems', 'itemsProcFuncConfig' => [ 'prototypeName' => 'cart-events', ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_event.php b/Configuration/TCA/tx_cartevents_domain_model_event.php index 827fc226..f30bc695 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_event.php +++ b/Configuration/TCA/tx_cartevents_domain_model_event.php @@ -2,8 +2,6 @@ defined('TYPO3') or die(); -use TYPO3\CMS\Core\Resource\File; - $_LLL_general = 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf'; $_LLL_ttc = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf'; $_LLL_cart = 'LLL:EXT:cart/Resources/Private/Language/locallang_db.xlf'; @@ -50,7 +48,7 @@ tax_class_id, event_dates, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_event.div.relations, - related_events, + related_events, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_event.div.categorization, tags, category, categories, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.tabs.access, @@ -257,7 +255,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -265,48 +262,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'image', - 'tablenames' => 'tx_cartevents_domain_model_event', - ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-image-types', ], ], @@ -316,7 +275,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ', pdf', 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -324,48 +282,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'files', - 'tablenames' => 'tx_cartevents_domain_model_event', + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], - ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-media-types', ], ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php index c58bccb8..fd789bd4 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php +++ b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php @@ -2,8 +2,6 @@ defined('TYPO3') or die(); -use TYPO3\CMS\Core\Resource\File; - $_LLL_general = 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf'; $_LLL_ttc = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf'; $_LLL_db = 'LLL:EXT:cart_events/Resources/Private/Language/locallang_db.xlf'; @@ -40,7 +38,7 @@ 'types' => [ '1' => [ 'showitem' => ' - sku, title, + sku, title, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_eventdate.div.informations, --palette--;' . $_LLL_tca . ':tx_cartevents_domain_model_eventdate.palettes.begin_and_end;begin_and_end, calendar_entries, location, lecturer, @@ -195,7 +193,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -203,48 +200,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'image', - 'tablenames' => 'tx_cartevents_domain_model_eventdate', - ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-image-types', ], ], @@ -254,7 +213,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ', pdf', 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -262,48 +220,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'files', - 'tablenames' => 'tx_cartevents_domain_model_eventdate', + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], - ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-media-types', ], ], diff --git a/Configuration/TSconfig/ContentElementWizard.tsconfig b/Configuration/TSconfig/ContentElementWizard.tsconfig index 1b06b103..5e9df778 100644 --- a/Configuration/TSconfig/ContentElementWizard.tsconfig +++ b/Configuration/TSconfig/ContentElementWizard.tsconfig @@ -1,43 +1,5 @@ [traverse(page, "doktype") == 186] -TCEFORM.tt_content.list_type { - removeItems := addToList(cartevents_events, cartevents_eventdates) -} + TCEFORM.tt_content.CType.removeItems := addToList(cartevents_listevents, cartevents_eventdates) [ELSE] -TCEFORM.tt_content.list_type { - removeItems := addToList(cartevents_singleevent) -} - -mod.wizards.newContentElement.wizardItems.plugins { - elements { - list_events { - iconIdentifier = ext-cartevents-wizard-icon-list - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.list_events.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.list_events.description - tt_content_defValues { - CType = list - list_type = cartevents_listevents - } - } - - show_events { - iconIdentifier = ext-cartevents-wizard-icon-show - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.show_event.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.show_event.description - tt_content_defValues { - CType = list - list_type = cartevents_showevent - } - } - - teaser_events { - iconIdentifier = ext-cartevents-wizard-icon-teaser - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.teaser_events.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.teaser_events.description - tt_content_defValues { - CType = list - list_type = cartevents_teaserevents - } - } - } -} + TCEFORM.tt_content.CType.removeItems := addToList(cartevents_singleevent) [END] diff --git a/Configuration/page.tsconfig b/Configuration/page.tsconfig new file mode 100644 index 00000000..97d6f2fa --- /dev/null +++ b/Configuration/page.tsconfig @@ -0,0 +1,3 @@ + + + diff --git a/README.md b/README.md index e71bf0c9..fe97bac1 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,20 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f7809fa0f2ab40118e263cb714212d13)](https://www.codacy.com/app/extcode/cart_events?utm_source=github.com&utm_medium=referral&utm_content=extcode/cart_events&utm_campaign=Badge_Grade) Cart is a small but powerful extension which "solely" adds a shopping cart to your TYPO3 installation. -Cart Events provides an own data storage for events. Events can be offered via a list and detail view and can be purchased via cart function of the Cart extension. +Cart Events provides an own data storage for events. ## 1. Features -- -- -- +* It provides events and their event dates which can be created in the TYPO3 backend. +* The data for those events are stored in own data tables. +* The data fields of the events fit many use cases for seminars, workshops, theatre + performances or generally date-related seat reservations. +* The events and their dates can be displayed on the website with a list view and a + detail view. +* It is possible to limit the number of bookable seats per event date or per price + category of an event date. +* As it extends EXT:cart are the products compatible with EXT:cart and can + therefore be be purchased with the cart functionality of EXT:cart. ## 2. Installation @@ -17,7 +24,7 @@ Cart Events provides an own data storage for events. Events can be offered via a #### Installation using Composer -The recommended way to install the extension is by using [Composer][2]. In your Composer based TYPO3 project root, just do `composer require extcode/cart-events`. +The recommended way to install the extension is by using [Composer][2]. In your Composer based TYPO3 project root, just do `composer require extcode/cart-events`. #### Installation as extension from TYPO3 Extension Repository (TER) @@ -35,9 +42,10 @@ Sometimes minor versions also result in minor adjustments to own templates or co | Cart Events | TYPO3 | PHP | Support/Development | |-------------|------------|-----------|--------------------------------------| -| 5.x.x | 12.4 | 8.1 - 8.4 | Features, Bugfixes, Security Updates | -| 4.x.x | 10.4, 11.5 | 7.2+ | Bugfixes, Security Updates | -| 3.x.x | 10.4 | 7.2 - 7.4 | Security Updates | +| 6.x.x | 13.4 | 8.2 - 8.4 | Features, Bugfixes, Security Updates | +| 5.x.x | 12.4 | 8.1 - 8.4 | Bugfixes, Security Updates | +| 4.x.x | 10.4, 11.5 | 7.2+ | Security Updates | +| 3.x.x | 10.4 | 7.2 - 7.4 | | | 2.x.x | 9.5 | 7.2 - 7.4 | | | 1.x.x | 8.7 | 7.0 - 7.4 | | @@ -62,4 +70,4 @@ News uses **semantic versioning** which basically means for you, that * [PayPal.Me](https://paypal.me/extcart) [1]: https://docs.typo3.org/typo3cms/extensions/cart_events/ -[2]: https://getcomposer.org/ \ No newline at end of file +[2]: https://getcomposer.org/ diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 03968d8f..91406791 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -16,18 +16,18 @@ The event could not be added to the shopping cart! - + Event is not bookable. - + Not enough Seats available. - + Invalid event date given. - + Invalid category price for event date given diff --git a/Resources/Public/JavaScripts/cart_events.js b/Resources/Public/JavaScripts/cart_events.js index 2bdfface..dace1d8c 100644 --- a/Resources/Public/JavaScripts/cart_events.js +++ b/Resources/Public/JavaScripts/cart_events.js @@ -1,54 +1,64 @@ -var cart_events = (function () { +(() => { + const eventDates = document.getElementsByClassName('event-event-date'); - var eventDates = document.getElementsByClassName('event-event-date'); - - for (var eventDateCount=0; eventDateCount < eventDates.length; eventDateCount++) { - var addToCartForms = eventDates[eventDateCount].querySelectorAll('form.add-to-cart-form'); - for (var addToCartFormsCount=0; addToCartFormsCount < addToCartForms.length; addToCartFormsCount++) { - var priceCategorySelects = addToCartForms[addToCartFormsCount].querySelectorAll('.price-category-select'); - for (var priceCategorySelectCount=0; priceCategorySelectCount < priceCategorySelects.length; priceCategorySelectCount++) { - - priceCategorySelects[priceCategorySelectCount].addEventListener('change', function(){ updatePriceCategory(this, eventDates[eventDateCount]) }, false); - } - } + for (let eventDateCount = 0; eventDateCount < eventDates.length; eventDateCount++) { + const addToCartForms = eventDates[eventDateCount].querySelectorAll('form.add-to-cart-form'); + for (let addToCartFormsCount = 0; addToCartFormsCount < addToCartForms.length; addToCartFormsCount++) { + const priceCategorySelects = addToCartForms[addToCartFormsCount].querySelectorAll('select.price-category-select'); + for (let priceCategorySelectCount = 0; priceCategorySelectCount < priceCategorySelects.length; priceCategorySelectCount++) { + priceCategorySelects[priceCategorySelectCount].addEventListener('change', function () { + updatePriceCategory(this, eventDates[eventDateCount]) + }, false); + } } + } - function updatePriceCategory(element, eventDate) { - var price; - - var style = element.selectedOptions[0].style.display; - eventDate.querySelectorAll('.event-date-price-category').forEach(el => { - el.style.display = 'none'; - }); - eventDate.querySelector('.event-date-price-category-' + element.selectedOptions[0].value).style.display = style; + function dispatchCustomEvent(name, dataObject) { + const customEvent = new CustomEvent( + name, + { + bubbles: true, + cancelable: true, + detail: dataObject + } + ); + document.dispatchEvent(customEvent); + } - var title = element.selectedOptions[0].getAttribute('data-title'); + function updatePriceCategory(element, eventDate) { + const title = element.selectedOptions[0].getAttribute('data-title'); - if (title) { - eventDate.querySelector('.event-date-price .regular-price').style.display = 'none'; - eventDate.querySelector('.event-date-price .special-price').style.display = 'block'; + if (title) { + eventDate.querySelector('.event-date-price .regular-price').style.display = 'none'; + eventDate.querySelector('.event-date-price .special-price').style.display = 'block'; - eventDate.querySelector('.event-date-price .special-price .title').innerHTML = title; + eventDate.querySelector('.event-date-price .special-price .title').innerHTML = title; - eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = ''; + eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = ''; - price = element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = price; + eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-regular-price'); - price = element.selectedOptions[0].getAttribute('data-special-price'); - eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = price; - } else { - eventDate.querySelector('.event-date-price .regular-price').style.display = 'block'; - eventDate.querySelector('.event-date-price .special-price').style.display = 'none'; + eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-special-price'); + } else { + eventDate.querySelector('.event-date-price .regular-price').style.display = 'block'; + eventDate.querySelector('.event-date-price .special-price').style.display = 'none'; - eventDate.querySelector('.event-date-price .special-price .title').innerHTML = ''; + eventDate.querySelector('.event-date-price .special-price .title').innerHTML = ''; - price = element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = price; + eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = ''; - eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = ''; - } + eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = ''; + eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = ''; } + dispatchCustomEvent( + 'extcode:update-price-category', + { + element, eventDate + } + ); + } })(); diff --git a/Tests/Acceptance/AddEventDateToCartCest.php b/Tests/Acceptance/AddEventDateToCartCest.php index b2c85ed9..8349d12d 100644 --- a/Tests/Acceptance/AddEventDateToCartCest.php +++ b/Tests/Acceptance/AddEventDateToCartCest.php @@ -26,7 +26,7 @@ public function addBookableEventToCart(Tester $I): void $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('19,99 €'); @@ -68,7 +68,7 @@ public function addBookableEventDateWithoutSeatHandlingToCart(Tester $I): void $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('19,99 €'); @@ -104,7 +104,7 @@ public function addBookableEventDateWithAvailableNumberOfSeatToCart(Tester $I): $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -149,7 +149,7 @@ public function addDifferentBookableEventDatesWithAvailableNumberOfSeatToCart(Te $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -203,7 +203,7 @@ public function addBookableEventDateWithAvailableNumberOfSeatButNotMoreToCart(Te $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -257,7 +257,7 @@ public function addDifferentBookableEventDateWithAvailableNumberOfSeatButNotMore $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -333,4 +333,120 @@ public function addDifferentBookableEventDateWithAvailableNumberOfSeatButNotMore $I->seeInField("input[name='tx_cart_cart[quantities][CartEvents_4]']", '8'); $I->see('314,06', '.checkout-product-table tr:nth-child(2) td:nth-child(4)'); } + + #[Test] + public function addBookableEventDateWithPriceCategoryAvailableNumberOfSeatToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with price category wit available number of seats to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Item was added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '7'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('7 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } + + #[Test] + public function addBookableEventDateWithPriceCategoryAvailableNumberOfSeatButNotMoreToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with price category with available number of seats but not more to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '38'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '38'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '37'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('37 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } + + #[Test] + public function addDifferentBookableEventDateWithPriceCategoryAvailableNumberOfSeatButNotMoreToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with different price categories with available number of seats but not more to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->wantTo('Add the price group "Category B" with quantity of 37 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '37'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('37 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->wantTo('Add the first price group "Category B" with quantity of 1 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->wantTo('Add the first price group "Category A" with quantity of 1 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category A'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Item was added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } } diff --git a/Tests/Acceptance/EventListCest.php b/Tests/Acceptance/EventListCest.php index 42445ae8..1caab50b 100644 --- a/Tests/Acceptance/EventListCest.php +++ b/Tests/Acceptance/EventListCest.php @@ -14,6 +14,8 @@ use Extcode\CartEvents\Tests\Acceptance\Support\Tester; use PHPUnit\Framework\Attributes\Test; +use function PHPUnit\Framework\assertSame; + class EventListCest { #[Test] @@ -21,11 +23,11 @@ public function listForEvents(Tester $I): void { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=1&cHash=4e06011cd740d94d7270b73b5e209c7b'); + $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=1&cHash=b94e793b120e29763527f801db80844c'); $I->see('Teaser 1'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->see('Teaser 2'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->see('Teaser 3'); $I->dontSee('Event 4'); @@ -36,7 +38,7 @@ public function detailViewForNonBookableEvent(Tester $I): void { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=1&cHash=4e06011cd740d94d7270b73b5e209c7b'); + $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=1&cHash=b94e793b120e29763527f801db80844c'); $I->click('Event 1'); $I->see('Event 1', 'h1'); @@ -49,7 +51,7 @@ public function detailViewForBookableEventWithOneEventdateWithoutPriceCategories { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('Event 2', 'h1'); @@ -69,7 +71,7 @@ public function detailViewForBookableEventWithTwoOrMoreEventdatesWithoutPriceCat { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->see('Event 3', 'h1'); @@ -91,4 +93,94 @@ public function detailViewForBookableEventWithTwoOrMoreEventdatesWithoutPriceCat $I->dontSee('This event date can not be booked.'); } + + #[Test] + public function detailViewForBookableEventWithPriceCategories(Tester $I): void + { + $I->wantToTest('I can see a bookable event with price categories and all options are rendered correctly.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->see('Event 5', 'h1'); + + $I->see('Eventdate 5', '.event-event-date:nth-child(1) > h2'); + $I->see('31.07.2024 10:00 - ', '.event-event-date:nth-child(1) > div.date'); + $I->see('Seats: 82 / 275', '.event-event-date:nth-child(1) > div'); + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->seeElement("select[name='tx_cart_cart[priceCategory]']"); + $I->dontSeeElement("select[name='tx_cart_cart[priceCategory]'] option[selected='selected']"); + + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(2)", + 'Category D', + '4', + '10,00 €' + ); + $I->see('Category D', "select[name='tx_cart_cart[priceCategory]'] option[disabled='']"); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(3)", + 'Category C', + '3', + '15,00 €' + ); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(4)", + 'Category B', + '2', + '17,00 €' + ); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(5)", + 'Category A', + '1', + '22,00 €' + ); + } + + #[Test] + public function selectOptionForBookableEventWithPriceCategoriesChangePrice(Tester $I): void + { + $I->wantToTest('I can see a bookable event with price categories and all options are rendered correctly.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->see('17,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category A'); + $I->see('22,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category D'); + $I->dontSeeElement("select[name='tx_cart_cart[priceCategory]'] option[selected='selected']"); + $I->see('22,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + } + + private function seeOption(Tester $I, string $selector, string $label, string $value, string $price): void + { + $I->see($label, $selector); + assertSame( + $value, + $I->grabValueFrom($selector) + ); + assertSame( + $price, + $I->grabAttributeFrom($selector, 'data-regular-price') + ); + } } diff --git a/Tests/Acceptance/Support/Environment.php b/Tests/Acceptance/Support/Environment.php index 3641f93a..336e8ca2 100644 --- a/Tests/Acceptance/Support/Environment.php +++ b/Tests/Acceptance/Support/Environment.php @@ -22,9 +22,9 @@ final class Environment extends BackendEnvironment 'typo3/cms-core', 'typo3/cms-backend', 'typo3/cms-extbase', - 'typo3/cms-frontend', 'typo3/cms-fluid', 'typo3/cms-fluid-styled-content', + 'typo3/cms-frontend', 'typo3/cms-install', ], 'testExtensionsToLoad' => [ diff --git a/Tests/Fixtures/ContentDatabase.php b/Tests/Fixtures/ContentDatabase.php index d8605615..3fe233ad 100644 --- a/Tests/Fixtures/ContentDatabase.php +++ b/Tests/Fixtures/ContentDatabase.php @@ -7,16 +7,14 @@ 0 => [ 'uid' => '1', 'pid' => '3', - 'CType' => 'list', - 'list_type' => 'cartevents_listevents', + 'CType' => 'cartevents_listevents', 'pages' => '7', 'pi_flexform' => ' Event->show;Event->list table 0 ', ], 1 => [ 'uid' => '2', 'pid' => '11', - 'CType' => 'list', - 'list_type' => 'cart_cart', + 'CType' => 'cart_cart', 'pages' => '', 'pi_flexform' => '', ], diff --git a/Tests/Fixtures/EventsDatabase.php b/Tests/Fixtures/EventsDatabase.php index 253f68bc..2f391782 100644 --- a/Tests/Fixtures/EventsDatabase.php +++ b/Tests/Fixtures/EventsDatabase.php @@ -48,6 +48,17 @@ 'audience' => '', 'path_segment' => 'event-4', ], + 4 => [ + 'uid' => '5', + 'pid' => '7', + 'sku' => 'event-5', + 'title' => 'Event 5', + 'teaser' => '', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-5', + ], ], 'tx_cartevents_domain_model_eventdate' => [ 0 => [ @@ -141,5 +152,64 @@ 'price' => 9.99, 'bookable' => true, ], + 6 => [ + 'uid' => '7', + 'pid' => '7', + 'event' => '5', + 'sku' => 'eventdate-5', + 'title' => 'Eventdate 5', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 9.99, + 'bookable' => true, + 'price_categorized' => true, + 'price_categories' => 4, + 'handle_seats' => true, + 'handle_seats_in_price_category' => true, + ], + ], + 'tx_cartevents_domain_model_pricecategory' => [ + 0 => [ + 'uid' => 1, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category A', + 'sku' => 'category-a', + 'price' => 22.0, + 'seats_number' => 80, + 'seats_taken' => 47, + ], + 1 => [ + 'uid' => 2, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category B', + 'sku' => 'category-b', + 'price' => 17.0, + 'seats_number' => 70, + 'seats_taken' => 33, + ], + 2 => [ + 'uid' => 3, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category C', + 'sku' => 'category-c', + 'price' => 15.0, + 'seats_number' => 110, + 'seats_taken' => 98, + ], + 3 => [ + 'uid' => 4, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category D', + 'sku' => 'category-d', + 'price' => 10.0, + 'seats_number' => 15, + 'seats_taken' => 15, + ], ], ]; diff --git a/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php b/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php new file mode 100644 index 00000000..5761a397 --- /dev/null +++ b/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php @@ -0,0 +1,214 @@ +testExtensionsToLoad[] = 'extcode/cart'; + $this->testExtensionsToLoad[] = 'extcode/cart-events'; + + parent::setUp(); + + $this->importPHPDataSet(__DIR__ . '/../../../../Fixtures/PagesDatabase.php'); + $this->importPHPDataSet(__DIR__ . '/../../../../Fixtures/EventsDatabase.php'); + } + + #[Test] + public function throwExceptionWithoutQuantityArgument(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741700244); + $this->expectExceptionMessage('Quantity argument is missing'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [], + [], + false + ); + } + + #[Test] + public function throwExceptionWithQuantityArgumentLowerThanZero(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741692900); + $this->expectExceptionMessage('Quantity argument is invalid'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => -1, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithoutEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741700304); + $this->expectExceptionMessage('Event date argument is missing'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNonNumericEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741692831); + $this->expectExceptionMessage('Event date argument is invalid'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 'a', + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNotExistingEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741693220); + $this->expectExceptionMessage('Event date not found'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 1000, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNotBookableExistingEventDate(): void + { + $this->expectException(NotBookableException::class); + $this->expectExceptionCode(1741693273); + $this->expectExceptionMessage('Event date not bookable'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 1, + ], + [], + false + ); + } + + #[Test] + public function getCartProductForValidQuantityAndBookableEventDate(): void + { + $productFactory = $this->getProductFactory(); + $product = $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 3, + ], + [ + 1 => new TaxClass( + 1, + '19 %', + 0.19, + 'normal' + ), + ], + false + ); + + self::assertSame( + 1, + $product->getQuantity() + ); + self::assertSame( + 3, + $product->getProductId() + ); + self::assertSame( + 'event-3 - eventdate-3-1', + $product->getSku() + ); + self::assertSame( + 'Event 3 - Eventdate 3.1', + $product->getTitle() + ); + + self::assertSame( + 29.99, + $product->getPrice() + ); + self::assertSame( + 29.99, + $product->getGross() + ); + self::assertSame( + 25.201680672268907, + $product->getNet() + ); + self::assertSame( + 4.7883193277310925, + $product->getTax() + ); + + self::assertTrue( + $product->isVirtualProduct() + ); + + self::assertFalse( + $product->isHandleStock() + ); + } + + private function getProductFactory(): ProductFactory + { + return GeneralUtility::makeInstance( + ProductFactory::class, + GeneralUtility::makeInstance(EventDateRepository::class), + GeneralUtility::makeInstance(PriceCategoryRepository::class), + ); + } +} diff --git a/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php b/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php index 9423b950..5a47c8d6 100644 --- a/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php +++ b/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php @@ -12,18 +12,18 @@ */ use Codappix\Typo3PhpDatasets\TestingFramework; -use Extcode\CartEvents\Domain\Repository\EventRepository; +use Extcode\CartEvents\Domain\Repository\EventDateRepository; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; -#[CoversClass(EventRepository::class)] +#[CoversClass(EventDateRepository::class)] class EventDateRepositoryTest extends FunctionalTestCase { use TestingFramework; - private EventRepository $eventRepository; + private EventDateRepository $eventDateRepository; public function setUp(): void { @@ -32,15 +32,175 @@ public function setUp(): void parent::setUp(); - $this->eventRepository = GeneralUtility::makeInstance(EventRepository::class); + $this->eventDateRepository = GeneralUtility::makeInstance(EventDateRepository::class); $this->importPHPDataSet(__DIR__ . '/../../../Fixtures/PagesDatabase.php'); $this->importPHPDataSet(__DIR__ . '/../../../Fixtures/EventsDatabase.php'); } #[Test] - public function findNextReturnsNext(): never + public function findNextReturnsNextForOnePid(): void { - self::markTestSkipped(); + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '7'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 4, + $eventDates[0]['uid'] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '9'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 6, + $eventDates[0]['uid'] + ); + } + + #[Test] + public function findNextReturnsNextForTwoPid(): void + { + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 6, + $eventDates[0]['uid'] + ); + } + + #[Test] + public function findNextsOnlyReturnsNextInFutureInCorrectOrder(): void + { + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(10, '7'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(10, '7'); + + self::assertCount( + 3, + $eventDates + ); + self::assertSame( + 4, + $eventDates[0]['uid'] + ); + self::assertSame( + 3, + $eventDates[1]['uid'] + ); + self::assertSame( + 5, + $eventDates[2]['uid'] + ); } } diff --git a/Tests/Functional/Domain/Repository/EventRepositoryTest.php b/Tests/Functional/Domain/Repository/EventRepositoryTest.php index bc1ac4bd..138abdbe 100644 --- a/Tests/Functional/Domain/Repository/EventRepositoryTest.php +++ b/Tests/Functional/Domain/Repository/EventRepositoryTest.php @@ -75,7 +75,7 @@ public function findDemandedByNewEventDemand(): void $events = $this->eventRepository->findDemanded($eventDemand); self::assertCount( - 3, + 4, $events ); } diff --git a/composer.json b/composer.json index cd8679d1..2abbaedf 100644 --- a/composer.json +++ b/composer.json @@ -46,13 +46,13 @@ } }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", "ext-json": "*", "ext-pdo": "*", - "extcode/cart": "^10.0", - "typo3/cms-core": "^12.4", - "typo3/cms-extbase": "^12.4", - "typo3/cms-fluid": "^12.4" + "extcode/cart": "^11.3", + "typo3/cms-core": "^13.4", + "typo3/cms-extbase": "^13.4", + "typo3/cms-fluid": "^13.4" }, "require-dev": { "codappix/typo3-php-datasets": "^1.5", @@ -62,9 +62,8 @@ "friendsofphp/php-cs-fixer": "^3.16", "helmich/typo3-typoscript-lint": "^3.1", "phpstan/phpstan": "^1.10", - "ssch/typo3-rector": "^2.6", - "typo3/cms-fluid-styled-content": "^12.4", - "typo3/cms-install": "^12.4", + "typo3/cms-fluid-styled-content": "^13.4", + "typo3/cms-install": "^13.4", "typo3/testing-framework": "^8.0" }, "scripts": { @@ -107,5 +106,8 @@ "@test:typoscript:lint", "@test:php" ] + }, + "suggest": { + "typo3/cms-form": "^13.4" } } diff --git a/ext_emconf.php b/ext_emconf.php index df0c1d33..1a919546 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -11,8 +11,8 @@ 'author_company' => 'extco.de UG (haftungsbeschränkt)', 'constraints' => [ 'depends' => [ - 'typo3' => '12.4.0-12.4.99', - 'cart' => '10.0.0', + 'typo3' => '13.4.0-13.4.99', + 'cart' => '11.3.0', ], 'conflicts' => [], 'suggests' => [], diff --git a/ext_localconf.php b/ext_localconf.php index 9746107b..3feab405 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -6,7 +6,6 @@ use Extcode\CartEvents\Hooks\DataHandler; use Extcode\CartEvents\Hooks\DatamapDataHandlerHook; use Extcode\CartEvents\Updates\SlugUpdater; -use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Extbase\Utility\ExtensionUtility; defined('TYPO3') or die(); @@ -23,7 +22,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -34,7 +34,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -45,7 +46,8 @@ ], [ EventController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -56,7 +58,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -67,15 +70,10 @@ ], [ EventDateController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); -// TSconfig - -ExtensionManagementUtility::addPageTSConfig(' - -'); - // Cart Hooks $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart']['CartEvents']['Form']['AddToCartFinisher'] = diff --git a/rector.php b/rector.php index 0561a399..be817ae0 100644 --- a/rector.php +++ b/rector.php @@ -23,11 +23,11 @@ ]) // uncomment to reach your current PHP version ->withPhpSets(php81: true) - ->withPhpVersion(PhpVersion::PHP_81) + ->withPhpVersion(PhpVersion::PHP_82) ->withSets([ Typo3SetList::CODE_QUALITY, Typo3SetList::GENERAL, - Typo3LevelSetList::UP_TO_TYPO3_12, + Typo3LevelSetList::UP_TO_TYPO3_13, ]) // To have a better analysis from PHPStan, we teach it here some more things ->withPHPStanConfigs([ @@ -38,8 +38,8 @@ ConvertImplicitVariablesToExplicitGlobalsRector::class, ]) ->withConfiguredRule(ExtEmConfRector::class, [ - ExtEmConfRector::PHP_VERSION_CONSTRAINT => '8.1.0-8.4.99', - ExtEmConfRector::TYPO3_VERSION_CONSTRAINT => '12.4.0-12.4.99', + ExtEmConfRector::PHP_VERSION_CONSTRAINT => '8.2.0-8.4.99', + ExtEmConfRector::TYPO3_VERSION_CONSTRAINT => '13.4.0-13.4.99', ExtEmConfRector::ADDITIONAL_VALUES_TO_BE_REMOVED => [], ]) // If you use withImportNames(), you should consider excluding some TYPO3 files. diff --git a/shell.nix b/shell.nix index 425e64c1..fd13db84 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,6 @@ { pkgs ? import { } - ,phpVersion ? "php81" + ,phpVersion ? "php82" }: let