feat: my&p staging url toggle

This commit is contained in:
Björn Fromme
2026-02-18 11:12:29 +01:00
parent b1ef3dd13c
commit 8be7609a14
13 changed files with 170 additions and 42 deletions
@@ -31,6 +31,7 @@ use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Model\Product;
use EP\EpProducts\Domain\Repository\ContingentRepository; use EP\EpProducts\Domain\Repository\ContingentRepository;
use EP\EpProducts\Service\ContingentDataService; use EP\EpProducts\Service\ContingentDataService;
use EP\EpProducts\Service\MyEpUrlResolverService;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxContingentController extends ActionController class AjaxContingentController extends ActionController
@@ -44,18 +45,24 @@ class AjaxContingentController extends ActionController
*/ */
protected $contingentDataService; protected $contingentDataService;
/**
* @var MyEpUrlResolverService
*/
protected $myEpUrlResolverService;
/** /**
* @param ContingentRepository $contingentRepository * @param ContingentRepository $contingentRepository
* @param ContingentDataService $contingentDataService * @param ContingentDataService $contingentDataService
* @param MyEpUrlResolverService $myEpUrlResolverService
*/ */
public function __construct public function __construct(
(
ContingentRepository $contingentRepository, ContingentRepository $contingentRepository,
ContingentDataService $contingentDataService ContingentDataService $contingentDataService,
) MyEpUrlResolverService $myEpUrlResolverService
{ ) {
$this->contingentRepository = $contingentRepository; $this->contingentRepository = $contingentRepository;
$this->contingentDataService = $contingentDataService; $this->contingentDataService = $contingentDataService;
$this->myEpUrlResolverService = $myEpUrlResolverService;
} }
public function initializeAction() public function initializeAction()
@@ -80,7 +87,7 @@ class AjaxContingentController extends ActionController
*/ */
public function roomsAction(Hotel $hotel, Product $product, \DateTime $dateFrom, \DateTime $dateTo) public function roomsAction(Hotel $hotel, Product $product, \DateTime $dateFrom, \DateTime $dateTo)
{ {
$myEpUrl = $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
$rooms = $this->contingentDataService->getAvailableRooms( $rooms = $this->contingentDataService->getAvailableRooms(
$dateFrom->format('Y-m-d'), $dateFrom->format('Y-m-d'),
$dateTo->format('Y-m-d'), $dateTo->format('Y-m-d'),
@@ -35,6 +35,7 @@ use EP\EpProducts\Domain\Repository\ProductRepository;
use EP\EpProducts\Service\ContingentDataService; use EP\EpProducts\Service\ContingentDataService;
use EP\EpProducts\Service\DateService; use EP\EpProducts\Service\DateService;
use EP\EpProducts\Service\FilterService; use EP\EpProducts\Service\FilterService;
use EP\EpProducts\Service\MyEpUrlResolverService;
use EP\EpProducts\Service\ResellerDataService; use EP\EpProducts\Service\ResellerDataService;
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait; use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
use League\Period\Period; use League\Period\Period;
@@ -77,6 +78,11 @@ class AjaxTableController extends ActionController
*/ */
protected $contingentDataService; protected $contingentDataService;
/**
* @var MyEpUrlResolverService
*/
protected $myEpUrlResolverService;
/** /**
* @param ProductRepository $productRepository * @param ProductRepository $productRepository
* @param InfoboxRepository $infoboxRepository * @param InfoboxRepository $infoboxRepository
@@ -84,15 +90,16 @@ class AjaxTableController extends ActionController
* @param FilterService $filterService * @param FilterService $filterService
* @param ResellerDataService $resellerDataService * @param ResellerDataService $resellerDataService
* @param ContingentDataService $contingentDataService * @param ContingentDataService $contingentDataService
* @param MyEpUrlResolverService $myEpUrlResolverService
*/ */
public function __construct public function __construct(
(
ProductRepository $productRepository, ProductRepository $productRepository,
InfoboxRepository $infoboxRepository, InfoboxRepository $infoboxRepository,
DateService $dateService, DateService $dateService,
FilterService $filterService, FilterService $filterService,
ResellerDataService $resellerDataService, ResellerDataService $resellerDataService,
ContingentDataService $contingentDataService ContingentDataService $contingentDataService,
MyEpUrlResolverService $myEpUrlResolverService
) { ) {
$this->productRepository = $productRepository; $this->productRepository = $productRepository;
$this->infoboxRepository = $infoboxRepository; $this->infoboxRepository = $infoboxRepository;
@@ -100,6 +107,7 @@ class AjaxTableController extends ActionController
$this->filterService = $filterService; $this->filterService = $filterService;
$this->resellerDataService = $resellerDataService; $this->resellerDataService = $resellerDataService;
$this->contingentDataService = $contingentDataService; $this->contingentDataService = $contingentDataService;
$this->myEpUrlResolverService = $myEpUrlResolverService;
} }
public function initializeDatesAction() public function initializeDatesAction()
@@ -119,21 +127,19 @@ class AjaxTableController extends ActionController
* @param Hotel $hotel * @param Hotel $hotel
* @param string|null $dateFrom * @param string|null $dateFrom
* @param string|null $dateTo * @param string|null $dateTo
* @param string|null $myEpUrl
*/ */
public function datestableAction( public function datestableAction(
Product $product, Product $product,
Hotel $hotel, Hotel $hotel,
string $dateFrom = null, string $dateFrom = null,
string $dateTo = null, string $dateTo = null
string $myEpUrl = null
): void { ): void {
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true); $forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids); $filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
$filterSettings['dateFrom'] = $dateFrom; $filterSettings['dateFrom'] = $dateFrom;
$filterSettings['dateTo'] = $dateTo; $filterSettings['dateTo'] = $dateTo;
$paCode = GeneralUtility::_GET('pa'); $paCode = GeneralUtility::_GET('pa');
$myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
$dates = $this->dateService->getDatesTable([ $dates = $this->dateService->getDatesTable([
'product' => $product, 'product' => $product,
@@ -153,7 +159,6 @@ class AjaxTableController extends ActionController
'date' => $date['dateUid'], 'date' => $date['dateUid'],
'paCode' => $paCode, 'paCode' => $paCode,
'singleDate' => true, 'singleDate' => true,
'myEpUrl' => $myEpUrl,
] ]
); );
} }
@@ -220,18 +225,16 @@ class AjaxTableController extends ActionController
* @param Hotel $hotel * @param Hotel $hotel
* @param Date $date * @param Date $date
* @param bool $singleDate * @param bool $singleDate
* @param string|null $myEpUrl
*/ */
public function pricetableAction( public function pricetableAction(
Product $product, Product $product,
Hotel $hotel, Hotel $hotel,
Date $date, Date $date,
bool $singleDate = false, bool $singleDate = false
string $myEpUrl = null
): void { ): void {
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true); $forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids); $filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
$myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
$priceTable = $this->dateService->getPriceTable([ $priceTable = $this->dateService->getPriceTable([
'product' => $product, 'product' => $product,
@@ -273,12 +276,11 @@ class AjaxTableController extends ActionController
/** /**
* @param Product $product * @param Product $product
* @param Hotel $hotel * @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); $nonBookableInfo = $this->getNonBookableInfobox($product);
$myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
$priceTable = $this->dateService->getPriceTable([ $priceTable = $this->dateService->getPriceTable([
'product' => $product, 'product' => $product,
@@ -300,9 +302,8 @@ class AjaxTableController extends ActionController
/** /**
* @param Product $product * @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']); $roomTypes = GeneralUtility::trimExplode(',', $this->settings['priceTableRoomTypes']);
$roomTypesPax = array_map(function ($type) { $roomTypesPax = array_map(function ($type) {
@@ -316,7 +317,7 @@ class AjaxTableController extends ActionController
return $label; return $label;
}, $roomTypes); }, $roomTypes);
$maxPax = max($roomTypesPax); $maxPax = max($roomTypesPax);
$myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
$priceTable = $this->dateService->getEventPriceTable($product, $maxPax, $myEpUrl); $priceTable = $this->dateService->getEventPriceTable($product, $maxPax, $myEpUrl);
$hasIncludedServices = $this->dateService->hasIncludedServices($priceTable); $hasIncludedServices = $this->dateService->hasIncludedServices($priceTable);
$nonBookableInfo = $this->getNonBookableInfobox($product); $nonBookableInfo = $this->getNonBookableInfobox($product);
@@ -343,18 +344,16 @@ class AjaxTableController extends ActionController
* @param Product $product * @param Product $product
* @param string $dateFrom * @param string $dateFrom
* @param string $dateTo * @param string $dateTo
* @param string|null $myEpUrl
*/ */
public function daytripRoomsAction( public function daytripRoomsAction(
Hotel $hotel, Hotel $hotel,
Product $product, Product $product,
string $dateFrom, string $dateFrom,
string $dateTo, string $dateTo
string $myEpUrl = null
): void { ): void {
$period = new Period($dateFrom, $dateTo); $period = new Period($dateFrom, $dateTo);
$nights = (int) $period->dateInterval()->format('%a') + 1; $nights = (int) $period->dateInterval()->format('%a') + 1;
$myEpUrl = $myEpUrl ?? $this->settings['myEpUrl']; $myEpUrl = $this->myEpUrlResolverService->resolve();
if (2 > $nights) { if (2 > $nights) {
$rooms = []; $rooms = [];
@@ -29,6 +29,7 @@ namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Model\Product;
use EP\EpProducts\Domain\Model\Reseller; use EP\EpProducts\Domain\Model\Reseller;
use EP\EpProducts\Service\MyEpUrlResolverService;
use EP\EpProducts\Service\ResellerDataService; use EP\EpProducts\Service\ResellerDataService;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -40,11 +41,20 @@ class ResellerController extends ActionController
protected $resellerDataService; 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->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); $productData = $this->resellerDataService->preprocessSingle($product, $reseller, $myEpUrl);
$this->view->assign('reseller', $reseller); $this->view->assign('reseller', $reseller);
@@ -0,0 +1,43 @@
<?php
namespace EP\EpProducts\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpFoundation\Cookie;
class StagingEnvironmentMiddleware implements MiddlewareInterface
{
private const COOKIE_NAME = 'ep_myep_staging';
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$queryParams = $request->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);
}
}
@@ -0,0 +1,39 @@
<?php
namespace EP\EpProducts\Service;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class MyEpUrlResolverService implements SingletonInterface
{
/** @var string */
private $productionUrl;
/** @var string */
private $stagingUrl;
public function __construct()
{
$config = GeneralUtility::makeInstance(ExtensionConfiguration::class);
$this->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;
}
}
@@ -20,5 +20,11 @@ return [
'typo3/cms-frontend/site', 'typo3/cms-frontend/site',
], ],
], ],
'ep/staging-environment' => [
'target' => \EP\EpProducts\Middleware\StagingEnvironmentMiddleware::class,
'after' => [
'typo3/cms-frontend/authentication',
],
],
], ],
]; ];
@@ -54,8 +54,6 @@ plugin.tx_epproducts {
searchLogIgnoreIps = searchLogIgnoreIps =
# cat=epproducts/140/120; type=string; label=Concept UIDs to trigger inquiry form on product detail page (CSV) # cat=epproducts/140/120; type=string; label=Concept UIDs to trigger inquiry form on product detail page (CSV)
groupInquiryFormConceptUids = groupInquiryFormConceptUids =
# cat=epproducts/140/200; type=string; label=MyE&P Base URL
myEpUrl = https://my.ep-reisen.de
} }
persistence { persistence {
# cat=epproducts/100/100; type=string; label=Default storage PID # cat=epproducts/100/100; type=string; label=Default storage PID
@@ -52,7 +52,6 @@ plugin.tx_epproducts {
groupsPriceToEmail = {$plugin.tx_epproducts.settings.groupsPriceToEmail} groupsPriceToEmail = {$plugin.tx_epproducts.settings.groupsPriceToEmail}
groupsPriceToName = {$plugin.tx_epproducts.settings.groupsPriceToName} groupsPriceToName = {$plugin.tx_epproducts.settings.groupsPriceToName}
groupInquiryFormConceptUids = {$plugin.tx_epproducts.settings.groupInquiryFormConceptUids} groupInquiryFormConceptUids = {$plugin.tx_epproducts.settings.groupInquiryFormConceptUids}
myEpUrl = {$plugin.tx_epproducts.settings.myEpUrl}
logoImage = {$plugin.tx_eptheme.settings.logoImage} logoImage = {$plugin.tx_eptheme.settings.logoImage}
themekey = {$plugin.tx_eptheme.settings.themekey} themekey = {$plugin.tx_eptheme.settings.themekey}
globalConceptCode = {$plugin.tx_eptheme.settings.globalConceptCode} globalConceptCode = {$plugin.tx_eptheme.settings.globalConceptCode}
@@ -2,7 +2,10 @@
searchPageUid = 0 searchPageUid = 0
# cat=MyEp; type=string; label=MyE&P URL # 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!) # cat=MyEpAPI; type=string; label=MyE&P API base URL (trailing slash!)
myEpApiBaseUrl = myEpApiBaseUrl =
@@ -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 = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
$iconRegistry->registerIcon( $iconRegistry->registerIcon(
'ep-logo', 'ep-logo',
@@ -313,6 +309,7 @@ $boot = function () {
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ref'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ref';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'pa'; $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[months]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[month]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[month]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[year]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_ajax[year]';
@@ -0,0 +1,28 @@
<?php
namespace EP\EpTheme\ViewHelpers;
use EP\EpProducts\Service\MyEpUrlResolverService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class MyEpUrlViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): string {
return GeneralUtility::makeInstance(MyEpUrlResolverService::class)->resolve();
}
}
@@ -79,7 +79,6 @@ plugin.tx_eptheme {
myEpApiEndpointUrl = {$plugin.tx_eptheme.settings.myEpApiEndpointUrl} myEpApiEndpointUrl = {$plugin.tx_eptheme.settings.myEpApiEndpointUrl}
myEpApiToken = {$plugin.tx_eptheme.settings.myEpApiToken} myEpApiToken = {$plugin.tx_eptheme.settings.myEpApiToken}
myEpApiTimeout = {$plugin.tx_eptheme.settings.myEpApiTimeout} myEpApiTimeout = {$plugin.tx_eptheme.settings.myEpApiTimeout}
myEpUrl = {$plugin.tx_epproducts.settings.myEpUrl}
facebookViewContentValues { facebookViewContentValues {
productUid = {$plugin.tx_eptheme.settings.facebookViewContentProductUid} productUid = {$plugin.tx_eptheme.settings.facebookViewContentProductUid}
nameInternal = {$plugin.tx_eptheme.settings.facebookViewContentNameInternal} nameInternal = {$plugin.tx_eptheme.settings.facebookViewContentNameInternal}
@@ -37,7 +37,7 @@
</div> </div>
</f:if> </f:if>
<f:if condition="{settings.myEpPageUid}"> <f:if condition="{settings.myEpPageUid}">
<f:link.typolink parameter="{settings.myEpUrl}" class="topnav__item" title="My E&amp;P"> <f:link.typolink parameter="{ep:myEpUrl()}" class="topnav__item" title="My E&amp;P">
{ep:icon(icon: 'user', class: 'w-5 h-5')} {ep:icon(icon: 'user', class: 'w-5 h-5')}
</f:link.typolink> </f:link.typolink>
</f:if> </f:if>