feat: integrate travelinfo with myep API to fetch travel data
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
services:
|
||||
web:
|
||||
external_links:
|
||||
- "ddev-router:myep-next.ddev.site"
|
||||
@@ -26,6 +26,7 @@
|
||||
"helhum/typo3-console": "^6.4",
|
||||
"kigkonsult/icalcreator": "^2.29",
|
||||
"league/csv": "^9.2",
|
||||
"league/oauth2-client": "^2.8",
|
||||
"league/period": "^4.9",
|
||||
"lochmueller/staticfilecache": "^12.5",
|
||||
"sjbr/sr-freecap": "^2.6",
|
||||
|
||||
Generated
+66
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f4b99aa9b07e56210c516195545a1d40",
|
||||
"content-hash": "4f59a07672e06df6976e44c1999be0f2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "b13/container",
|
||||
@@ -1747,6 +1747,71 @@
|
||||
],
|
||||
"time": "2022-01-04T00:13:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-client",
|
||||
"version": "2.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-client.git",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
|
||||
"php": "^7.1 || >=8.0.0 <8.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.5",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.4",
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "^3.11"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "[email protected]",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"homepage": "https://github.com/shadowhand",
|
||||
"role": "Contributor"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 2.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"single sign on"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth2-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
|
||||
},
|
||||
"time": "2025-02-26T04:37:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/period",
|
||||
"version": "4.12.0",
|
||||
|
||||
@@ -255,28 +255,25 @@ routeEnhancers:
|
||||
- 2736
|
||||
routes:
|
||||
-
|
||||
routePath: '/{travel_info}/{travel_date}'
|
||||
_controller: 'Travelinfo::index'
|
||||
routePath: '/{travel_info}'
|
||||
_controller: 'Travelinfo::travelInfo'
|
||||
_arguments:
|
||||
travel_info: travelinfo
|
||||
travel_date: travelDate
|
||||
travel_info: travelInfo
|
||||
-
|
||||
routePath: '/{travel_code}'
|
||||
_controller: 'Travelinfo::travelCode'
|
||||
_arguments:
|
||||
travel_code: travelCode
|
||||
defaultController: 'Travelinfo::index'
|
||||
defaultController: 'Travelinfo::travelInfo'
|
||||
requirements:
|
||||
travel_code: '[A-Z]+\d{6}$'
|
||||
aspects:
|
||||
travel_info:
|
||||
type: PersistedAliasMapper
|
||||
tableName: tx_epproducts_domain_model_travelinfo
|
||||
routeFieldName: path_segment
|
||||
travel_date:
|
||||
type: PassthroughMapper
|
||||
travel_code:
|
||||
type: PassthroughMapper
|
||||
requirements:
|
||||
travel_date: '\d{4}\-\d{2}\-\d{2}|\d{2}\d{2}\d{4}'
|
||||
PageTypeSuffix:
|
||||
type: PageType
|
||||
default: /
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace EP\EpProducts\Controller;
|
||||
|
||||
use EP\EpProducts\Domain\Model\Date;
|
||||
use EP\EpProducts\Domain\Model\Travelinfo;
|
||||
use EP\EpProducts\Domain\Repository\DateRepository;
|
||||
use EP\EpProducts\Domain\Repository\TravelinfoRepository;
|
||||
use EP\EpProducts\MyEP\ApiClient;
|
||||
use EP\EpProducts\MyEP\ApiException;
|
||||
use TYPO3\CMS\Core\Http\ImmediateResponseException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
@@ -14,52 +14,34 @@ use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
class TravelinfoController extends ActionController
|
||||
{
|
||||
/**
|
||||
* @var DateRepository
|
||||
* @var ApiClient
|
||||
*/
|
||||
private $dateRepository;
|
||||
private $apiClient;
|
||||
|
||||
/**
|
||||
* @var TravelinfoRepository
|
||||
*/
|
||||
private $travelinfoRepository;
|
||||
|
||||
public function __construct(
|
||||
DateRepository $dateRepository,
|
||||
TravelinfoRepository $travelinfoRepository
|
||||
) {
|
||||
$this->dateRepository = $dateRepository;
|
||||
public function __construct(ApiClient $apiClient, TravelinfoRepository $travelinfoRepository)
|
||||
{
|
||||
$this->apiClient = $apiClient;
|
||||
$this->travelinfoRepository = $travelinfoRepository;
|
||||
}
|
||||
|
||||
public function travelCodeAction(string $travelCode)
|
||||
{
|
||||
$travelCode = strtoupper($travelCode);
|
||||
$travelCodeDate = str_replace('-', '/', $travelCode);
|
||||
// find specific travel info by code including travel date first
|
||||
$travelInfo = $this->travelinfoRepository->findOneByTravelCode($travelCode);
|
||||
|
||||
/** @var Date $date */
|
||||
$date = $this->dateRepository->findOneByCode($travelCodeDate);
|
||||
|
||||
if (null === $date) {
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$GLOBALS['TYPO3_REQUEST'],
|
||||
'Termin nicht gefunden'
|
||||
);
|
||||
throw new ImmediateResponseException($response, 1731071856);
|
||||
// find general travel info by code without travel date as fallback
|
||||
if (null === $travelInfo) {
|
||||
$travelCodeBase = substr($travelCode, 0, -6);
|
||||
$travelInfo = $this->travelinfoRepository->findOneByTravelCode($travelCodeBase);
|
||||
}
|
||||
|
||||
$product = $date->getProduct();
|
||||
|
||||
if (null === $product) {
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$GLOBALS['TYPO3_REQUEST'],
|
||||
'Produkt nicht gefunden'
|
||||
);
|
||||
throw new ImmediateResponseException($response, 1731071856);
|
||||
}
|
||||
|
||||
$travelinfo = $this->travelinfoRepository->findOneByProduct($product);
|
||||
|
||||
if (null === $travelinfo) {
|
||||
// return 404 in case no travel info is available as last resort
|
||||
if (null === $travelInfo) {
|
||||
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$GLOBALS['TYPO3_REQUEST'],
|
||||
'Reiseinformationen nicht gefunden'
|
||||
@@ -67,22 +49,39 @@ class TravelinfoController extends ActionController
|
||||
throw new ImmediateResponseException($response, 1731071856);
|
||||
}
|
||||
|
||||
$this->view->assign('travelinfo', $travelinfo);
|
||||
$this->view->assign('date', $date);
|
||||
$this->handleRequest($travelCode, $travelInfo);
|
||||
}
|
||||
|
||||
public function indexAction(Travelinfo $travelinfo, string $travelDate)
|
||||
public function travelInfoAction(Travelinfo $travelInfo)
|
||||
{
|
||||
$product = $travelinfo->getProduct();
|
||||
$travelCode = $travelInfo->getTravelCode();
|
||||
|
||||
if (preg_match('/^\d{2}\d{2}\d{4}$/', $travelDate)) {
|
||||
$travelDate = (\DateTimeImmutable::createFromFormat('dmY', $travelDate))->format('Y-m-d');
|
||||
$this->handleRequest($travelCode, $travelInfo);
|
||||
}
|
||||
|
||||
private function handleRequest(string $travelCode, Travelinfo $travelInfo)
|
||||
{
|
||||
|
||||
// fetch travel data via API call
|
||||
try {
|
||||
$travelData = $this->apiClient->getTravel($travelCode);
|
||||
} catch (ApiException $e) {
|
||||
$travelData = [];
|
||||
}
|
||||
|
||||
/** @var Date $date */
|
||||
$date = $this->dateRepository->findForProductAndDate($product, $travelDate)->getFirst();
|
||||
// extract courses from additional services
|
||||
$courses = array_filter($travelData['additionalServices'] ?? [], function ($service) {
|
||||
return 'KUR' === $service['subType'];
|
||||
});
|
||||
|
||||
$this->view->assign('travelinfo', $travelinfo);
|
||||
$this->view->assign('date', $date);
|
||||
// extract board services from additional services
|
||||
$board = array_filter($travelData['additionalServices'] ?? [], function ($service) {
|
||||
return 'VPF' === $service['subType'];
|
||||
});
|
||||
|
||||
$this->view->assign('travelInfo', $travelInfo);
|
||||
$this->view->assign('travelData', $travelData);
|
||||
$this->view->assign('courses', $courses);
|
||||
$this->view->assign('board', $board);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace EP\EpProducts\MyEP;
|
||||
|
||||
use League\OAuth2\Client\Provider\AbstractProvider;
|
||||
use League\OAuth2\Client\Provider\GenericProvider;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
class ApiClient
|
||||
{
|
||||
private static ?AccessToken $accessToken = null;
|
||||
|
||||
/**
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getLastUpdateAt(): ?\DateTimeImmutable
|
||||
{
|
||||
$httpClient = $this->getHttpClient();
|
||||
|
||||
try {
|
||||
$response = $httpClient->request('GET', 'last-update');
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (200 !== $response->getStatusCode()) {
|
||||
throw new ApiException($response->getStatusCode());
|
||||
}
|
||||
|
||||
$data = $response->toArray(false);
|
||||
|
||||
if (null === $data['timestamp'] ?? null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new \DateTimeImmutable($data['timestamp']);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getPickups(): array
|
||||
{
|
||||
$httpClient = $this->getHttpClient();
|
||||
|
||||
try {
|
||||
$response = $httpClient->request('GET', 'pickups');
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (200 !== $response->getStatusCode()) {
|
||||
throw new ApiException($response->getStatusCode());
|
||||
}
|
||||
|
||||
return $response->toArray(false);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getTravels(): array
|
||||
{
|
||||
$httpClient = $this->getHttpClient();
|
||||
|
||||
try {
|
||||
$response = $httpClient->request('GET', 'travels');
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (200 !== $response->getStatusCode()) {
|
||||
throw new ApiException($response->getStatusCode());
|
||||
}
|
||||
|
||||
return $response->toArray(false);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getTravel(string $productCode): array
|
||||
{
|
||||
$httpClient = $this->getHttpClient();
|
||||
|
||||
try {
|
||||
$response = $httpClient->request('GET', 'travels/' . $productCode);
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (200 !== $response->getStatusCode()) {
|
||||
throw new ApiException($response->getStatusCode());
|
||||
}
|
||||
|
||||
return $response->toArray(false);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function getHttpClient(): HttpClientInterface
|
||||
{
|
||||
$config = GeneralUtility::makeInstance(ExtensionConfiguration::class)
|
||||
->get('ep_products');
|
||||
|
||||
if (null === static::$accessToken || true === static::$accessToken->hasExpired()) {
|
||||
static::$accessToken = $this
|
||||
->getProvider($config)
|
||||
->getAccessToken('client_credentials')
|
||||
;
|
||||
}
|
||||
|
||||
return HttpClient::createForBaseUri($config['myEpApiBaseUrl'], [
|
||||
'auth_bearer' => static::$accessToken->getToken(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getProvider(array $config): AbstractProvider
|
||||
{
|
||||
return new GenericProvider([
|
||||
'clientId' => $config['myEpApiClientId'],
|
||||
'clientSecret' => $config['myEpApiClientSecret'],
|
||||
'redirectUri' => null,
|
||||
'urlAuthorize' => $config['myEpApiUrlAuthorize'],
|
||||
'urlAccessToken' => $config['myEpApiUrlToken'],
|
||||
'urlResourceOwnerDetails' => null,
|
||||
'scopes' => 'api',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace EP\EpProducts\MyEP;
|
||||
|
||||
class ApiException extends \Exception
|
||||
{
|
||||
}
|
||||
+6
-25
@@ -27,7 +27,7 @@ return [
|
||||
],
|
||||
],
|
||||
'palettes' => [
|
||||
'basics' => ['showitem' => 'title,path_segment,--linebreak--,product,hide_address,--linebreak--,links,
|
||||
'basics' => ['showitem' => 'title,path_segment,--linebreak--,travel_code,hide_address,--linebreak--,links,
|
||||
--linebreak--,teasers'],
|
||||
'text' => ['showitem' => 'subline,headline,--linebreak--,intro_text,--linebreak--, bus_info_text,--linebreak--,
|
||||
departure_text,--linebreak--,footer_text,--linebreak--,self_arranged_text,--linebreak--,additional_info,
|
||||
@@ -191,32 +191,13 @@ return [
|
||||
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
|
||||
),
|
||||
],
|
||||
'product' => [
|
||||
'travel_code' => [
|
||||
'exclude' => false,
|
||||
'label' => 'Produkt',
|
||||
'label' => 'Reisecode',
|
||||
'config' => [
|
||||
'type' => 'group',
|
||||
'internal_type' => 'db',
|
||||
'allowed' => 'tx_epproducts_domain_model_product',
|
||||
'minitems' => 1,
|
||||
'maxitems' => 1,
|
||||
'size' => 1,
|
||||
'fieldControl' => [
|
||||
'elementBrowser' => [
|
||||
'disabled' => true,
|
||||
],
|
||||
],
|
||||
'fieldWizard' => [
|
||||
'recordsOverview' => [
|
||||
'disabled' => true,
|
||||
],
|
||||
],
|
||||
'suggestOptions' => [
|
||||
'default' => [
|
||||
'additionalSearchFields' => 'code,name,keywords',
|
||||
'orderBy' => 'code',
|
||||
],
|
||||
],
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'eval' => 'trim,required'
|
||||
],
|
||||
],
|
||||
'hide_address' => [
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
# customsubcategory=100=General
|
||||
# cat=epproducts/100/100; type=int; label=Searchresult PID
|
||||
# cat=General; type=int; label=Searchresult PID
|
||||
searchPageUid = 0
|
||||
|
||||
# cat=MyEpAPI; type=string; label=MyE&P API base URL (trailing slash!)
|
||||
myEpApiBaseUrl =
|
||||
|
||||
# cat=MyEpAPI; type=string; label=MyE&P API client id
|
||||
myEpApiClientId =
|
||||
|
||||
# cat=MyEpAPI; type=string; label=MyE&P API client secret
|
||||
myEpApiClientSecret =
|
||||
|
||||
# cat=MyEpAPI; type=string; label=MyE&P API authorization URL
|
||||
myEpApiUrlAuthorize =
|
||||
|
||||
# cat=MyEpAPI; type=string; label=MyE&P API token URL
|
||||
myEpApiUrlToken =
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ $boot = function () {
|
||||
'EP.ep_products',
|
||||
'travelinfo',
|
||||
[
|
||||
'Travelinfo' => 'index,travelCode',
|
||||
'Travelinfo' => 'travelInfo,travelCode',
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<f:link.typolink parameter="{f:if(condition: settings.logoLink, then: settings.logoLink, else: settings.defaultHomeUid)}"
|
||||
class="block lg:pb-4"
|
||||
title="zur Startseite">
|
||||
<f:image src="Logo"
|
||||
<f:image src="{settings.logoImage}"
|
||||
class="block w-auto {ep:themeClasses(classes: '{ep: \'h-10 lg:h-12\', sbw: \'h-14 lg:h-24\', snz: \'h-14 lg:h-24\', suz: \'h-14 lg:h-24\', uch: \'h-12 lg:h-20\', ser: \'h-10 lg:h-12\'}', key: settings.themekey)}"
|
||||
alt="Logo"/>
|
||||
alt="Bildmarke"/>
|
||||
</f:link.typolink>
|
||||
<f:link.typolink parameter="{settings.myEpPageUid}"
|
||||
title="My E&P"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<f:section name="Main">
|
||||
<div class="container relative">
|
||||
<div class="h-80 lg:h-128">
|
||||
<f:image image="{travelinfo.headerImage}"
|
||||
<f:image image="{travelInfo.headerImage}"
|
||||
width="1280"
|
||||
alt="Reiseinformationen"
|
||||
class="block w-full h-full object-cover object-center" />
|
||||
@@ -18,8 +18,8 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4">
|
||||
<f:variable name="bgColors" value="{0: 'bg-[#165883]/90', 1: 'bg-[#9DBACE]/90', 2: 'bg-[#3396E4]/90', 3: 'bg-[#0268AA]/90'}"/>
|
||||
<f:variable name="linksCount" value="{travelinfo.links -> f:count()}"/>
|
||||
<f:for each="{travelinfo.links}" as="link" iteration="iteration">
|
||||
<f:variable name="linksCount" value="{travelInfo.links -> f:count()}"/>
|
||||
<f:for each="{travelInfo.links}" as="link" iteration="iteration">
|
||||
<f:render section="Link" arguments="{item: link, color: '{bgColors.{iteration.index}}'}"/>
|
||||
</f:for>
|
||||
<a href="#goodtoknow"
|
||||
@@ -40,13 +40,13 @@
|
||||
<div class="pb-32 pt-32 lg:pt-52 -mt-24 bg-ep-primary-bg">
|
||||
<div class="container px-8 lg:px-24" data-rte-content>
|
||||
<h1 class="headline--underlined headline--has-subline">
|
||||
<span class="subline">{travelinfo.subline}</span>
|
||||
{travelinfo.headline}
|
||||
<span class="subline">{travelInfo.subline}</span>
|
||||
{travelInfo.headline}
|
||||
</h1>
|
||||
{travelinfo.introText -> f:format.html()}
|
||||
{travelInfo.introText -> f:format.html()}
|
||||
<f:if condition="{date}">
|
||||
<p>
|
||||
{date.product.name} {date.dateStart -> f:format.date(format: 'd.m.Y')} - {date.dateEnd -> f:format.date(format: 'd.m.Y')}
|
||||
{travelData.label} {travelData.dateFrom -> f:format.date(format: 'd.m.Y')} - {travelData.dateTo -> f:format.date(format: 'd.m.Y')}
|
||||
</p>
|
||||
</f:if>
|
||||
</div>
|
||||
@@ -63,20 +63,22 @@
|
||||
</div>
|
||||
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
|
||||
<f:if condition="{date}">
|
||||
<f:if condition="{travelinfo.hideAddress}">
|
||||
<f:if condition="{travelInfo.hideAddress}">
|
||||
<f:else>
|
||||
<h3 class="text-white">
|
||||
Adresse
|
||||
</h3>
|
||||
<p>
|
||||
{date.hotel.name}
|
||||
{travelData.hotel.name}
|
||||
<br>
|
||||
{date.hotel.address -> f:format.nl2br()}
|
||||
{travelData.hotel.street}
|
||||
<br>
|
||||
{travelData.hotel.country} {travelData.hotel.city}
|
||||
</p>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:if condition="{date.pickups -> f:count()} > 0">
|
||||
<f:if condition="{date.pickups -> f:count()} <= 3">
|
||||
<f:if condition="{travelData.pickupsTo -> f:count()} > 0">
|
||||
<f:if condition="{travelData.pickupsTo -> f:count()} <= 3">
|
||||
<f:then>
|
||||
<f:render section="Pickups" arguments="{_all}"/>
|
||||
</f:then>
|
||||
@@ -98,11 +100,11 @@
|
||||
</f:if>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<f:if condition="{travelinfo.selfArrangedText}">
|
||||
<f:if condition="{travelInfo.selfArrangedText}">
|
||||
<h3 class="text-white">
|
||||
Eigenanreise
|
||||
</h3>
|
||||
{travelinfo.selfArrangedText -> f:format.html()}
|
||||
{travelInfo.selfArrangedText -> f:format.html()}
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,13 +116,13 @@
|
||||
</h2>
|
||||
</div>
|
||||
<div class="container px-4 lg:px-24 flex flex-col space-y-16 pb-16">
|
||||
<f:for each="{travelinfo.teasers}" as="teaser">
|
||||
<f:for each="{travelInfo.teasers}" as="teaser">
|
||||
<f:render section="Teaser" arguments="{_all}"/>
|
||||
</f:for>
|
||||
</div>
|
||||
<div id="goodtoknow" class="bg-ep-primary-bg">
|
||||
<div class="container">
|
||||
<f:if condition="{travelinfo.additionalInfo}">
|
||||
<f:if condition="{travelInfo.additionalInfo}">
|
||||
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#B48F1D] px-8 lg:px-0 py-8">
|
||||
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
|
||||
<div class="w-32 h-32 xl:w-16 xl:h-16 text-[#F9C700]">
|
||||
@@ -131,11 +133,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
|
||||
{travelinfo.additionalInfo -> f:format.html()}
|
||||
{travelInfo.additionalInfo -> f:format.html()}
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:if condition="{travelinfo.importantPhoneNumbers}">
|
||||
<f:if condition="{travelInfo.importantPhoneNumbers}">
|
||||
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#F8C62F] px-8 lg:px-0 py-8">
|
||||
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
|
||||
<div class="text-2xl lg:text-4xl text-white uppercase font-bold">
|
||||
@@ -143,18 +145,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
|
||||
<f:if condition="{date.guide}">
|
||||
<f:if condition="{travelData.guide}">
|
||||
<p>
|
||||
<strong>Busbegleitung / Reiseleitung</strong>
|
||||
<br>
|
||||
{date.guide.name} {date.guide.phone}
|
||||
{travelData.guide.name} {travelData.guide.phone}
|
||||
</p>
|
||||
</f:if>
|
||||
{travelinfo.importantPhoneNumbers -> f:format.html()}
|
||||
{travelInfo.importantPhoneNumbers -> f:format.html()}
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:if condition="{travelinfo.importantLinks}">
|
||||
<f:if condition="{travelInfo.importantLinks}">
|
||||
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#18527B] px-8 lg:px-0 py-8">
|
||||
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
|
||||
<div class="text-2xl lg:text-4xl text-white uppercase font-bold">
|
||||
@@ -162,18 +164,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
|
||||
{travelinfo.importantLinks -> f:format.html()}
|
||||
{travelInfo.importantLinks -> f:format.html()}
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container px-4 lg:px-24 py-16">
|
||||
{travelinfo.footerText -> f:format.html()}
|
||||
{travelInfo.footerText -> f:format.html()}
|
||||
<f:image src="EXT:ep_theme/Resources/Public/images/signature_ep_team.png" class="block w-full max-w-64 h-auto mt-8" alt="" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<f:image image="{travelinfo.footerImage}" width="1028" class="block w-full h-auto" alt="" />
|
||||
<f:image image="{travelInfo.footerImage}" width="1028" class="block w-full h-auto" alt="" />
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
@@ -196,20 +198,28 @@
|
||||
<h2 class="headline--primary">
|
||||
{teaser.title}
|
||||
</h2>
|
||||
<f:if condition="{date}">
|
||||
<f:if condition="{travelData}">
|
||||
<f:if condition="{teaser.category} == 'food'">
|
||||
<p>
|
||||
{date.board}
|
||||
</p>
|
||||
<f:if condition="{board -> f:count()} > 0">
|
||||
<f:then>
|
||||
<ul>
|
||||
<f:for as="item" each="{board}">
|
||||
<li>
|
||||
{item.label}
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</f:then>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<f:if condition="{teaser.category} == 'course'">
|
||||
<f:if condition="{date.activeCoursesForTravelinfo -> f:count()} > 0">
|
||||
<f:if condition="{courses -> f:count()} > 0">
|
||||
<f:then>
|
||||
<p>
|
||||
<strong>Folgende Kurse finden statt</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<f:for as="course" each="{date.activeCoursesForTravelinfo}">
|
||||
<f:for as="course" each="{courses}">
|
||||
<li>
|
||||
{course.label}
|
||||
</li>
|
||||
@@ -257,7 +267,7 @@
|
||||
<p>
|
||||
Bitte findet euch mindestens <strong>20 Minuten vor Abfahrt</strong> an der gebuchten Haltestelle ein.
|
||||
</p>
|
||||
{travelinfo.busInfoText -> f:format.html()}
|
||||
{travelInfo.busInfoText -> f:format.html()}
|
||||
<table class="w-full bg-zinc-200 text-zinc-800 text-xs md:text-base mb-4">
|
||||
<tr>
|
||||
<th class="border-r border-white/50 px-2 py-1 md:p-2 text-left text-sm md:text-base">
|
||||
@@ -267,18 +277,18 @@
|
||||
Datum/Uhrzeit
|
||||
</th>
|
||||
</tr>
|
||||
<f:for each="{date.pickups}" as="pickup">
|
||||
<f:for each="{travelData.pickupsTo}" as="pickup">
|
||||
<tr class="odd:bg-zinc-50 align-top">
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
{pickup.city}<br><span class="text-xs">{pickup.street}</span>
|
||||
</td>
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
{pickup.date}<br>{f:if(condition: pickup.time, then: '{pickup.time} Uhr', else: '???')}
|
||||
{pickup.time -> f:format.date(format: 'd.m.Y')}<br>{pickup.time -> f:format.date(format: 'H:i')} Uhr
|
||||
</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
</table>
|
||||
{travelinfo.departureText -> f:format.html()}
|
||||
{travelInfo.departureText -> f:format.html()}
|
||||
</f:section>
|
||||
|
||||
<f:section name="Icon">
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
|
||||
<f:section name="Main">
|
||||
<f:render partial="Travelinfo" section="Main" arguments="{_all}"/>
|
||||
</f:section>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user