From 8be7609a141f7a958e08cbd171c6a4a7e58bf911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 18 Feb 2026 11:12:29 +0100 Subject: [PATCH] feat: my&p staging url toggle --- .../Controller/AjaxContingentController.php | 19 +++++--- .../Controller/AjaxTableController.php | 43 +++++++++---------- .../Classes/Controller/ResellerController.php | 18 ++++++-- .../StagingEnvironmentMiddleware.php | 43 +++++++++++++++++++ .../Service/MyEpUrlResolverService.php | 39 +++++++++++++++++ .../Configuration/RequestMiddlewares.php | 6 +++ .../TypoScript/constants.typoscript | 2 - .../Configuration/TypoScript/setup.typoscript | 1 - .../ext/ep_products/ext_conf_template.txt | 5 ++- .../ext/ep_products/ext_localconf.php | 5 +-- .../Classes/ViewHelpers/MyEpUrlViewHelper.php | 28 ++++++++++++ .../Configuration/TypoScript/setup.typoscript | 1 - .../Resources/Private/Partials/Topnav.html | 2 +- 13 files changed, 170 insertions(+), 42 deletions(-) create mode 100644 public/typo3conf/ext/ep_products/Classes/Middleware/StagingEnvironmentMiddleware.php create mode 100644 public/typo3conf/ext/ep_products/Classes/Service/MyEpUrlResolverService.php create mode 100644 public/typo3conf/ext/ep_theme/Classes/ViewHelpers/MyEpUrlViewHelper.php diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxContingentController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxContingentController.php index 7dc183da..265ed1d7 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxContingentController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxContingentController.php @@ -31,6 +31,7 @@ use EP\EpProducts\Domain\Model\Hotel; use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Repository\ContingentRepository; use EP\EpProducts\Service\ContingentDataService; +use EP\EpProducts\Service\MyEpUrlResolverService; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; class AjaxContingentController extends ActionController @@ -44,18 +45,24 @@ class AjaxContingentController extends ActionController */ protected $contingentDataService; + /** + * @var MyEpUrlResolverService + */ + protected $myEpUrlResolverService; + /** * @param ContingentRepository $contingentRepository * @param ContingentDataService $contingentDataService + * @param MyEpUrlResolverService $myEpUrlResolverService */ - public function __construct - ( + public function __construct( ContingentRepository $contingentRepository, - ContingentDataService $contingentDataService - ) - { + ContingentDataService $contingentDataService, + MyEpUrlResolverService $myEpUrlResolverService + ) { $this->contingentRepository = $contingentRepository; $this->contingentDataService = $contingentDataService; + $this->myEpUrlResolverService = $myEpUrlResolverService; } public function initializeAction() @@ -80,7 +87,7 @@ class AjaxContingentController extends ActionController */ public function roomsAction(Hotel $hotel, Product $product, \DateTime $dateFrom, \DateTime $dateTo) { - $myEpUrl = $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $rooms = $this->contingentDataService->getAvailableRooms( $dateFrom->format('Y-m-d'), $dateTo->format('Y-m-d'), diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxTableController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxTableController.php index cc203d11..30008cb9 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxTableController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxTableController.php @@ -35,6 +35,7 @@ use EP\EpProducts\Domain\Repository\ProductRepository; use EP\EpProducts\Service\ContingentDataService; use EP\EpProducts\Service\DateService; use EP\EpProducts\Service\FilterService; +use EP\EpProducts\Service\MyEpUrlResolverService; use EP\EpProducts\Service\ResellerDataService; use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait; use League\Period\Period; @@ -77,6 +78,11 @@ class AjaxTableController extends ActionController */ protected $contingentDataService; + /** + * @var MyEpUrlResolverService + */ + protected $myEpUrlResolverService; + /** * @param ProductRepository $productRepository * @param InfoboxRepository $infoboxRepository @@ -84,15 +90,16 @@ class AjaxTableController extends ActionController * @param FilterService $filterService * @param ResellerDataService $resellerDataService * @param ContingentDataService $contingentDataService + * @param MyEpUrlResolverService $myEpUrlResolverService */ - public function __construct - ( + public function __construct( ProductRepository $productRepository, InfoboxRepository $infoboxRepository, DateService $dateService, FilterService $filterService, ResellerDataService $resellerDataService, - ContingentDataService $contingentDataService + ContingentDataService $contingentDataService, + MyEpUrlResolverService $myEpUrlResolverService ) { $this->productRepository = $productRepository; $this->infoboxRepository = $infoboxRepository; @@ -100,6 +107,7 @@ class AjaxTableController extends ActionController $this->filterService = $filterService; $this->resellerDataService = $resellerDataService; $this->contingentDataService = $contingentDataService; + $this->myEpUrlResolverService = $myEpUrlResolverService; } public function initializeDatesAction() @@ -119,21 +127,19 @@ class AjaxTableController extends ActionController * @param Hotel $hotel * @param string|null $dateFrom * @param string|null $dateTo - * @param string|null $myEpUrl */ public function datestableAction( Product $product, Hotel $hotel, string $dateFrom = null, - string $dateTo = null, - string $myEpUrl = null + string $dateTo = null ): void { $forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true); $filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids); $filterSettings['dateFrom'] = $dateFrom; $filterSettings['dateTo'] = $dateTo; $paCode = GeneralUtility::_GET('pa'); - $myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $dates = $this->dateService->getDatesTable([ 'product' => $product, @@ -153,7 +159,6 @@ class AjaxTableController extends ActionController 'date' => $date['dateUid'], 'paCode' => $paCode, 'singleDate' => true, - 'myEpUrl' => $myEpUrl, ] ); } @@ -220,18 +225,16 @@ class AjaxTableController extends ActionController * @param Hotel $hotel * @param Date $date * @param bool $singleDate - * @param string|null $myEpUrl */ public function pricetableAction( Product $product, Hotel $hotel, Date $date, - bool $singleDate = false, - string $myEpUrl = null + bool $singleDate = false ): void { $forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true); $filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids); - $myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $priceTable = $this->dateService->getPriceTable([ 'product' => $product, @@ -273,12 +276,11 @@ class AjaxTableController extends ActionController /** * @param Product $product * @param Hotel $hotel - * @param string|null $myEpUrl */ - public function pricetableHtmlAction(Product $product, Hotel $hotel, string $myEpUrl = null): void + public function pricetableHtmlAction(Product $product, Hotel $hotel): void { $nonBookableInfo = $this->getNonBookableInfobox($product); - $myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $priceTable = $this->dateService->getPriceTable([ 'product' => $product, @@ -300,9 +302,8 @@ class AjaxTableController extends ActionController /** * @param Product $product - * @param string|null $myEpUrl */ - public function eventPricetableAction(Product $product, string $myEpUrl = null): void + public function eventPricetableAction(Product $product): void { $roomTypes = GeneralUtility::trimExplode(',', $this->settings['priceTableRoomTypes']); $roomTypesPax = array_map(function ($type) { @@ -316,7 +317,7 @@ class AjaxTableController extends ActionController return $label; }, $roomTypes); $maxPax = max($roomTypesPax); - $myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $priceTable = $this->dateService->getEventPriceTable($product, $maxPax, $myEpUrl); $hasIncludedServices = $this->dateService->hasIncludedServices($priceTable); $nonBookableInfo = $this->getNonBookableInfobox($product); @@ -343,18 +344,16 @@ class AjaxTableController extends ActionController * @param Product $product * @param string $dateFrom * @param string $dateTo - * @param string|null $myEpUrl */ public function daytripRoomsAction( Hotel $hotel, Product $product, string $dateFrom, - string $dateTo, - string $myEpUrl = null + string $dateTo ): void { $period = new Period($dateFrom, $dateTo); $nights = (int) $period->dateInterval()->format('%a') + 1; - $myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; + $myEpUrl = $this->myEpUrlResolverService->resolve(); if (2 > $nights) { $rooms = []; diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/ResellerController.php b/public/typo3conf/ext/ep_products/Classes/Controller/ResellerController.php index 61eae5c3..24e2f191 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/ResellerController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/ResellerController.php @@ -29,6 +29,7 @@ namespace EP\EpProducts\Controller; use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Model\Reseller; +use EP\EpProducts\Service\MyEpUrlResolverService; use EP\EpProducts\Service\ResellerDataService; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; @@ -40,11 +41,20 @@ class ResellerController extends ActionController protected $resellerDataService; /** - * @param ResellerDataService $resellerDataService + * @var MyEpUrlResolverService */ - public function __construct(ResellerDataService $resellerDataService) - { + protected $myEpUrlResolverService; + + /** + * @param ResellerDataService $resellerDataService + * @param MyEpUrlResolverService $myEpUrlResolverService + */ + public function __construct( + ResellerDataService $resellerDataService, + MyEpUrlResolverService $myEpUrlResolverService + ) { $this->resellerDataService = $resellerDataService; + $this->myEpUrlResolverService = $myEpUrlResolverService; } /** @@ -80,7 +90,7 @@ class ResellerController extends ActionController ); } - $myEpUrl = $this->settings['myEpUrl'] ?? 'https://my.ep-reisen.de'; + $myEpUrl = $this->myEpUrlResolverService->resolve(); $productData = $this->resellerDataService->preprocessSingle($product, $reseller, $myEpUrl); $this->view->assign('reseller', $reseller); diff --git a/public/typo3conf/ext/ep_products/Classes/Middleware/StagingEnvironmentMiddleware.php b/public/typo3conf/ext/ep_products/Classes/Middleware/StagingEnvironmentMiddleware.php new file mode 100644 index 00000000..f71aec78 --- /dev/null +++ b/public/typo3conf/ext/ep_products/Classes/Middleware/StagingEnvironmentMiddleware.php @@ -0,0 +1,43 @@ +getQueryParams(); + + if (!isset($queryParams['ep_staging'])) { + return $handler->handle($request); + } + + $isStaging = '1' === (string) $queryParams['ep_staging']; + + // Update $_COOKIE so the value is available during the current request + if ($isStaging) { + $_COOKIE[self::COOKIE_NAME] = '1'; + } else { + unset($_COOKIE[self::COOKIE_NAME]); + } + + $response = $handler->handle($request); + + // Set the cookie on the response for subsequent browser requests + if ($isStaging) { + $cookie = new Cookie(self::COOKIE_NAME, '1', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX); + } else { + $cookie = new Cookie(self::COOKIE_NAME, '', 1, '/', null, false, true, false, Cookie::SAMESITE_LAX); + } + + return $response->withAddedHeader('Set-Cookie', (string) $cookie); + } +} diff --git a/public/typo3conf/ext/ep_products/Classes/Service/MyEpUrlResolverService.php b/public/typo3conf/ext/ep_products/Classes/Service/MyEpUrlResolverService.php new file mode 100644 index 00000000..b6868cbb --- /dev/null +++ b/public/typo3conf/ext/ep_products/Classes/Service/MyEpUrlResolverService.php @@ -0,0 +1,39 @@ +productionUrl = (string) $config->get('ep_products', 'myEpUrl'); + $this->stagingUrl = (string) $config->get('ep_products', 'myEpStagingUrl'); + } + + /** + * Returns the staging URL when the `ep_myep_staging` cookie is set and a staging URL + * is configured. Falls back to the production URL in all other cases. + * + * Both URLs are read from the extension configuration (Extension Manager), + * bypassing TypoScript to avoid constant-override collisions. + */ + public function resolve(): string + { + if ('' !== $this->stagingUrl && !empty($_COOKIE['ep_myep_staging'])) { + return $this->stagingUrl; + } + + return $this->productionUrl; + } +} diff --git a/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php b/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php index cc8a137e..0ba3b6ca 100644 --- a/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php +++ b/public/typo3conf/ext/ep_products/Configuration/RequestMiddlewares.php @@ -20,5 +20,11 @@ return [ 'typo3/cms-frontend/site', ], ], + 'ep/staging-environment' => [ + 'target' => \EP\EpProducts\Middleware\StagingEnvironmentMiddleware::class, + 'after' => [ + 'typo3/cms-frontend/authentication', + ], + ], ], ]; diff --git a/public/typo3conf/ext/ep_products/Configuration/TypoScript/constants.typoscript b/public/typo3conf/ext/ep_products/Configuration/TypoScript/constants.typoscript index 0009fe37..ec2ab3dc 100644 --- a/public/typo3conf/ext/ep_products/Configuration/TypoScript/constants.typoscript +++ b/public/typo3conf/ext/ep_products/Configuration/TypoScript/constants.typoscript @@ -54,8 +54,6 @@ plugin.tx_epproducts { searchLogIgnoreIps = # cat=epproducts/140/120; type=string; label=Concept UIDs to trigger inquiry form on product detail page (CSV) groupInquiryFormConceptUids = - # cat=epproducts/140/200; type=string; label=MyE&P Base URL - myEpUrl = https://my.ep-reisen.de } persistence { # cat=epproducts/100/100; type=string; label=Default storage PID diff --git a/public/typo3conf/ext/ep_products/Configuration/TypoScript/setup.typoscript b/public/typo3conf/ext/ep_products/Configuration/TypoScript/setup.typoscript index e620ba7a..a494397c 100644 --- a/public/typo3conf/ext/ep_products/Configuration/TypoScript/setup.typoscript +++ b/public/typo3conf/ext/ep_products/Configuration/TypoScript/setup.typoscript @@ -52,7 +52,6 @@ plugin.tx_epproducts { groupsPriceToEmail = {$plugin.tx_epproducts.settings.groupsPriceToEmail} groupsPriceToName = {$plugin.tx_epproducts.settings.groupsPriceToName} groupInquiryFormConceptUids = {$plugin.tx_epproducts.settings.groupInquiryFormConceptUids} - myEpUrl = {$plugin.tx_epproducts.settings.myEpUrl} logoImage = {$plugin.tx_eptheme.settings.logoImage} themekey = {$plugin.tx_eptheme.settings.themekey} globalConceptCode = {$plugin.tx_eptheme.settings.globalConceptCode} diff --git a/public/typo3conf/ext/ep_products/ext_conf_template.txt b/public/typo3conf/ext/ep_products/ext_conf_template.txt index 35bf821f..243ddffe 100644 --- a/public/typo3conf/ext/ep_products/ext_conf_template.txt +++ b/public/typo3conf/ext/ep_products/ext_conf_template.txt @@ -2,7 +2,10 @@ searchPageUid = 0 # cat=MyEp; type=string; label=MyE&P URL -myEpUrl = +myEpUrl = https://my.ep-reisen.de + +# cat=MyEp; type=string; label=MyE&P Staging URL +myEpStagingUrl = https://my.ep-reisen.net # cat=MyEpAPI; type=string; label=MyE&P API base URL (trailing slash!) myEpApiBaseUrl = diff --git a/public/typo3conf/ext/ep_products/ext_localconf.php b/public/typo3conf/ext/ep_products/ext_localconf.php index 19b6b92e..7c65103b 100644 --- a/public/typo3conf/ext/ep_products/ext_localconf.php +++ b/public/typo3conf/ext/ep_products/ext_localconf.php @@ -35,10 +35,6 @@ $boot = function () { ], ]; - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptConstants( - 'plugin.tx_epproducts.settings.myEpUrl = '.$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ep_products']['myEpUrl'] - ); - $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class); $iconRegistry->registerIcon( 'ep-logo', @@ -313,6 +309,7 @@ $boot = function () { $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ref'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'pa'; + $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ep_staging'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[months]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[month]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[year]'; diff --git a/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/MyEpUrlViewHelper.php b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/MyEpUrlViewHelper.php new file mode 100644 index 00000000..bd6a8740 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/MyEpUrlViewHelper.php @@ -0,0 +1,28 @@ +resolve(); + } +} diff --git a/public/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.typoscript b/public/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.typoscript index 25e0fa0f..3c8025e0 100644 --- a/public/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.typoscript +++ b/public/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.typoscript @@ -79,7 +79,6 @@ plugin.tx_eptheme { myEpApiEndpointUrl = {$plugin.tx_eptheme.settings.myEpApiEndpointUrl} myEpApiToken = {$plugin.tx_eptheme.settings.myEpApiToken} myEpApiTimeout = {$plugin.tx_eptheme.settings.myEpApiTimeout} - myEpUrl = {$plugin.tx_epproducts.settings.myEpUrl} facebookViewContentValues { productUid = {$plugin.tx_eptheme.settings.facebookViewContentProductUid} nameInternal = {$plugin.tx_eptheme.settings.facebookViewContentNameInternal} diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Topnav.html b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Topnav.html index 95136606..f28f42d7 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Topnav.html +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Topnav.html @@ -37,7 +37,7 @@ - + {ep:icon(icon: 'user', class: 'w-5 h-5')}