Merge branch 'feature/date-limited-teasers' into develop

This commit is contained in:
Björn Fromme
2020-02-15 18:26:48 +01:00
16 changed files with 115 additions and 80 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ php_version: "7.3"
webserver_type: apache-cgi webserver_type: apache-cgi
router_http_port: "80" router_http_port: "80"
router_https_port: "443" router_https_port: "443"
xdebug_enabled: true xdebug_enabled: false
additional_hostnames: additional_hostnames:
- ep-events - ep-events
- ski-boarderweek - ski-boarderweek
@@ -15,7 +15,7 @@ additional_hostnames:
- ski-wochenenden - ski-wochenenden
- schnee-event - schnee-event
additional_fqdns: [] additional_fqdns: []
nfs_mount_enabled: true nfs_mount_enabled: false
provider: default provider: default
omit_containers: [dba] omit_containers: [dba]
use_dns_when_possible: true use_dns_when_possible: true
+11
View File
@@ -109,6 +109,12 @@ routeEnhancers:
_arguments: _arguments:
product_uid: product product_uid: product
hotel_uid: hotel hotel_uid: hotel
-
routePath: '/detail/{date_from}/{date_to}'
_controller: 'Product::detail'
_arguments:
date_from: dateFrom
date_to: dateTo
defaultController: 'Product::detail' defaultController: 'Product::detail'
aspects: aspects:
product_uid: product_uid:
@@ -119,6 +125,11 @@ routeEnhancers:
type: PersistedAliasMapper type: PersistedAliasMapper
tableName: tx_epproducts_domain_model_hotel tableName: tx_epproducts_domain_model_hotel
routeFieldName: uid routeFieldName: uid
requirements:
product_uid: '\d'
hotel_uid: '\d'
date_from: '\d{4}-\d{2}-\d{2}'
date_to: '\d{4}-\d{2}-\d{2}'
BookingLinkPlugin: BookingLinkPlugin:
type: Extbase type: Extbase
extension: EpProducts extension: EpProducts
@@ -95,11 +95,13 @@ class AjaxDateController extends ActionController
* @param Product $product * @param Product $product
* @param Hotel $hotel * @param Hotel $hotel
* @param string $paCode * @param string $paCode
* @param string $dateFrom
* @param string $dateTo
* *
* @return string * @return string
* @throws \Doctrine\DBAL\DBALException * @throws \Doctrine\DBAL\DBALException
*/ */
public function datesAction(Product $product, Hotel $hotel = null, $paCode = null) public function datesAction(Product $product, Hotel $hotel = null, $paCode = null, $dateFrom = null, $dateTo = null)
{ {
if ($hotel === null) { if ($hotel === null) {
return json_encode([ return json_encode([
@@ -111,7 +113,12 @@ class AjaxDateController extends ActionController
} }
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true); $forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$filterSettings = $this->filterService->getDefaultFilterSettings($forcedConceptUids, false); $filterSettings = $this->filterService->getDefaultFilterSettings(
$forcedConceptUids,
false,
$dateFrom,
$dateTo
);
$altProductLink = null; $altProductLink = null;
$altLabel = null; $altLabel = null;
@@ -100,12 +100,16 @@ class ProductController extends ActionController
/** /**
* @param Product $product * @param Product $product
* @param Hotel $hotel * @param Hotel $hotel
* @param string $dateFrom
* @param string $dateTo
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
*/ */
public function detailAction( public function detailAction(
Product $product = null, Product $product = null,
Hotel $hotel = null Hotel $hotel = null,
$dateFrom = null,
$dateTo = null
) { ) {
// Get product to display from flexform settings if not provided via url // Get product to display from flexform settings if not provided via url
if ($product === null) { if ($product === null) {
@@ -142,6 +146,7 @@ class ProductController extends ActionController
$hotel = reset($product->getHotels()->toArray()); $hotel = reset($product->getHotels()->toArray());
} }
} }
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']); $excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
$isProductWithExcludedConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(), $isProductWithExcludedConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(),
$excludedConceptUids, false); $excludedConceptUids, false);
@@ -154,6 +159,8 @@ class ProductController extends ActionController
'isProductWithExcludedConcept' => $isProductWithExcludedConcept, 'isProductWithExcludedConcept' => $isProductWithExcludedConcept,
'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept, 'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept,
'currentPageUid' => $GLOBALS['TSFE']->id, 'currentPageUid' => $GLOBALS['TSFE']->id,
'dateFrom' => $dateFrom,
'dateTo' => $dateTo,
]); ]);
} }
@@ -193,9 +200,13 @@ class ProductController extends ActionController
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true); $excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
$limit = $this->settings['showMoreLink'] ? 4 : 0; $limit = $this->settings['showMoreLink'] ? 4 : 0;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit); $teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
$dateFrom = $this->settings['dateFrom'] ? date('Y-m-d', $this->settings['dateFrom']) : null;
$dateTo = $this->settings['dateTo'] ? date('Y-m-d', $this->settings['dateTo']) : null;
$this->view->assign('teasers', $teasers); $this->view->assign('teasers', $teasers);
$this->view->assign('dateAuto', $dateAuto); $this->view->assign('dateAuto', $dateAuto);
$this->view->assign('filterSettings', $filterSettings); $this->view->assign('filterSettings', $filterSettings);
$this->view->assign('dateFrom', $dateFrom);
$this->view->assign('dateTo', $dateTo);
} }
} }
@@ -282,7 +282,7 @@ class ProductRepository extends AbstractRepository
]) ])
; ;
$this->addFiltersettingsConditions($query, $filterSettings); $this->addFiltersettingsConditions($query, $filterSettings, true);
return $query->execute()->fetchAll(); return $query->execute()->fetchAll();
} }
@@ -60,15 +60,25 @@ class FilterService implements SingletonInterface
/** /**
* @param array $forcedConceptUids * @param array $forcedConceptUids
* @param bool $respectBookableDaterange * @param bool $respectBookableDaterange
* * @param string $dateFromYmd
* @param string $dateToYmd
* @return FilterSettings * @return FilterSettings
* @throws \Exception
*/ */
public function getDefaultFilterSettings(array $forcedConceptUids = [], $respectBookableDaterange = true) public function getDefaultFilterSettings(array $forcedConceptUids = [], $respectBookableDaterange = true, $dateFromYmd = null, $dateToYmd = null)
{ {
[ $dateFrom, $dateTo ] = $this->constrainDateRange($this->getDateRange()); [ $dateFrom, $dateTo ] = $this->constrainDateRange($this->getDateRange());
if (!$respectBookableDaterange) {
if (null !== $dateFromYmd) {
$dateFrom = new \DateTime($dateFromYmd);
} elseif (!$respectBookableDaterange) {
$dateFrom = new \DateTime('now'); $dateFrom = new \DateTime('now');
} }
if (null !== $dateToYmd) {
$dateTo = new \DateTime($dateToYmd);
}
$filterSettings = new FilterSettings($dateFrom, $dateTo); $filterSettings = new FilterSettings($dateFrom, $dateTo);
if ($forcedConceptUids) { if ($forcedConceptUids) {
$filterSettings->setConceptUids($forcedConceptUids); $filterSettings->setConceptUids($forcedConceptUids);
@@ -180,8 +180,7 @@ class ProductDataService implements SingletonInterface
*/ */
public function createTeaserData(array $teaserData) public function createTeaserData(array $teaserData)
{ {
$hotelCategory = array_key_exists($teaserData['hotelCategory'], Hotel::$categoryLabels) ? $hotelCategory = Hotel::$categoryLabels[$teaserData['hotelCategory']] ?? null;
Hotel::$categoryLabels[$teaserData['hotelCategory']] : null;
// Use compound key, hotel is not unique // Use compound key, hotel is not unique
$key = $teaserData['hotelUid'] . ':' . $teaserData['productUid']; $key = $teaserData['hotelUid'] . ':' . $teaserData['productUid'];
@@ -95,7 +95,9 @@ plugin.tx_epproducts {
} }
} }
} }
features.requireCHashArgumentForActionArguments = 0 features {
requireCHashArgumentForActionArguments = 0
}
} }
config.tx_extbase.persistence.classes { config.tx_extbase.persistence.classes {
@@ -404,7 +404,8 @@ $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'][] = 'tx_epproducts_searchresult[searchParams]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_searchresult[searchParams]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_searchresult[filterSettings]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_searchresult[filterSettings]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_product_detail[filterSettings]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_product_detail[dateFrom]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_product_detail[dateTo]';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_reseller[reseller]'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'tx_epproducts_reseller[reseller]';
$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]';
@@ -27,6 +27,7 @@ namespace EP\EpTheme\ViewHelpers\Uri;
* This copyright notice MUST APPEAR in all copies of the script! * This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/ ***************************************************************/
use EP\EpEvents\Domain\Model\Hotel;
use EP\EpProducts\Domain\Model\Product; use EP\EpProducts\Domain\Model\Product;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
@@ -42,12 +43,12 @@ class ProductViewHelper extends AbstractViewHelper
{ {
parent::initializeArguments(); parent::initializeArguments();
$this->registerArgument('product', Product::class, 'The product', true); $this->registerArgument('product', 'array', 'The product');
$this->registerArgument('hotel', 'mixed', 'The hotel'); $this->registerArgument('hotel', 'array', 'The hotel');
$this->registerArgument('linkHotel', 'bool', 'Whether to link the hotel', false, false); $this->registerArgument('dateFrom', 'string', 'Date to limit table on detail page by (Y-m-d)');
$this->registerArgument('referringPageUid', 'int', 'Uid of the referring page'); $this->registerArgument('dateTo', 'string', 'Date to limit table on detail page by (Y-m-d)');
$this->registerArgument('defaultDetailPageUid', 'int', 'Uid of default page for product details'); $this->registerArgument('forceHotelUid', 'bool', 'Whether to force link a hotel');
$this->registerArgument('origin', 'string', 'Origin of the link'); $this->registerArgument('slug', 'string', 'Slug to append to resulting uri');
} }
/** /**
@@ -63,35 +64,45 @@ class ProductViewHelper extends AbstractViewHelper
) )
{ {
$product = $arguments['product']; $product = $arguments['product'];
$hotel = $arguments['hotel'];
if ($product->getDetailPage()) { $linkArguments = null;
if ($arguments['forceHotelUid']) {
$linkArguments = [
'product' => $product['uid'],
'hotel' => $hotel['uid'],
];
} elseif ($arguments['dateFrom'] || $arguments['dateTo']) {
$linkArguments = [
'dateFrom' => $arguments['dateFrom'],
'dateTo' => $arguments['dateTo'],
];
}
if (null !== $linkArguments) {
$uri = $renderingContext
->getControllerContext()
->getUriBuilder()
->reset()
->setTargetPageUid($product['detailPage'])
->uriFor('detail', $linkArguments, 'Product', 'epproducts', 'product_detail')
;
} else {
/** @var ContentObjectRenderer $contentObject */ /** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class); $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
return $contentObject->typoLink_URL( $uri = $contentObject->typoLink_URL(
[ [
'parameter' => $product->getDetailPage(), 'parameter' => $product['detailPage'],
] ]
); );
} }
$pageUid = $arguments['defaultDetailPageUid']; if ($arguments['slug']) {
$uriArguments = [ 'product' => $product ]; $uri .= $arguments['slug'].'/';
if ((int) $arguments['referringPageUid'] > 0) {
$uriArguments['referringPageUid'] = $arguments['referringPageUid'];
} }
if ($arguments['origin'] !== null) {
$uriArguments['origin'] = $arguments['origin']; return $uri;
}
if ((bool) $arguments['linkHotel'] && $arguments['hotel'] !== null) {
$uriArguments['hotel'] = $arguments['hotel'];
}
return $renderingContext
->getControllerContext()
->getUriBuilder()
->reset()
->setTargetPageUid($pageUid)
->uriFor('detail', $uriArguments, 'Product', 'epproducts', 'product_detail')
;
} }
} }
@@ -78,6 +78,14 @@
daytrip: { daytrip: {
type: Number, type: Number,
default: 0 default: 0
},
dateFrom: {
type: String,
default: null
},
dateTo: {
type: String,
default: null
} }
}, },
computed: { computed: {
@@ -91,12 +99,15 @@
this.datesShow = true; this.datesShow = true;
this.priceTableShow = false; this.priceTableShow = false;
this.detailShow = false; this.detailShow = false;
const data = $.param({
'tx_epproducts_ajax[paCode]': this.$store.state.config.paCode,
'tx_epproducts_ajax[dateFrom]': this.dateFrom,
'tx_epproducts_ajax[dateTo]': this.dateTo
});
axios({ axios({
url: this.uris['dates'], url: this.uris['dates'],
method: 'post', method: 'post',
data: { data: data
paCode: this.$store.state.config.paCode
}
}).then(response => { }).then(response => {
const json = response.data; const json = response.data;
if (json.dates.length === 1) { if (json.dates.length === 1) {
@@ -6,30 +6,9 @@
<f:section name="Teaser"> <f:section name="Teaser">
<div class="teaserbox teaserbox--product teaserbox--flippable"> <div class="teaserbox teaserbox--product teaserbox--flippable">
<f:if condition="{teaser.forceHotelUid}"> <a href="{ep:uri.product(forceHotelUid: teaser.forceHotelUid, product: teaser.product, hotel: teaser.hotel, dateFrom: dateFrom, dateTo: dateTo, slug: slug)}" class="teaserbox__flip" title="{teaser.product.name}">
<f:then> <f:render section="Inner" arguments="{_all}"/>
<f:link.action </a>
pageUid="{teaser.product.detailPage}"
action="detail"
controller="Product"
arguments="{product: teaser.product.uid, hotel: teaser.hotel.uid}"
extensionName="epproducts"
pluginName="product_detail"
class="teaserbox__flip"
title="{teaser.product.name}">
<f:render section="Inner" arguments="{_all}"/>
</f:link.action>
</f:then>
<f:else>
<f:variable name="teaserUrl" value="{f:uri.typolink(parameter: teaser.product.detailPage)}"/>
<f:if condition="{slug}">
<f:variable name="teaserUrl" value="{teaserUrl}{slug}/"/>
</f:if>
<a href="{teaserUrl}" class="teaserbox__flip" title="{teaser.product.name}">
<f:render section="Inner" arguments="{_all}"/>
</a>
</f:else>
</f:if>
</div> </div>
</f:section> </f:section>
@@ -5,17 +5,7 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser"> <f:section name="Teaser">
<f:if condition="{teaser.forceHotelUid}"> {ep:uri.product(forceHotelUid: teaser.forceHotelUid, product: teaser.product, hotel: teaser.hotel, dateFrom: dateFrom, dateTo: dateTo, slug: slug) -> f:variable(name: 'teaserUrl')}
<f:then>
<f:variable name="teaserUrl" value="{f:uri.action(pageUid: teaser.product.detailPage, action: 'detail', controller: 'Product', arguments: '{product: teaser.product.uid, hotel: teaser.hotel.uid}', extensionName: 'epproducts', pluginName: 'product_detail')}"/>
</f:then>
<f:else>
<f:variable name="teaserUrl" value="{f:uri.typolink(parameter: teaser.product.detailPage)}"/>
<f:if condition="{slug}">
<f:variable name="teaserUrl" value="{teaserUrl}{slug}/"/>
</f:if>
</f:else>
</f:if>
<f:render section="Inner" arguments="{_all}"/> <f:render section="Inner" arguments="{_all}"/>
</f:section> </f:section>
@@ -20,6 +20,8 @@
:bus-pro-id="{product.busProId -> f:format.raw()}" :bus-pro-id="{product.busProId -> f:format.raw()}"
name-internal="{product.nameInternal -> f:format.raw()}" name-internal="{product.nameInternal -> f:format.raw()}"
:daytrip="{product.daytrip -> v:variable.convert(type: 'int')}" :daytrip="{product.daytrip -> v:variable.convert(type: 'int')}"
date-from="{dateFrom}"
date-to="{dateTo}"
inline-template> inline-template>
<article> <article>
<f:render partial="Product/HeaderDetail" arguments="{_all}"/> <f:render partial="Product/HeaderDetail" arguments="{_all}"/>
@@ -12,11 +12,11 @@
<f:for each="{teasers}" as="teaser"> <f:for each="{teasers}" as="teaser">
<f:if condition="{settings.layout} == 'rows'"> <f:if condition="{settings.layout} == 'rows'">
<f:then> <f:then>
<f:render partial="Product/TeaserWide" section="Teaser" arguments="{teaser: teaser}"/> <f:render partial="Product/TeaserWide" section="Teaser" arguments="{teaser: teaser, dateFrom: dateFrom, dateTo: dateTo}"/>
</f:then> </f:then>
<f:else> <f:else>
<div class="col-md-3"> <div class="col-md-3">
<f:render partial="Product/Teaser" section="Teaser" arguments="{teaser: teaser}"/> <f:render partial="Product/Teaser" section="Teaser" arguments="{teaser: teaser, dateFrom: dateFrom, dateTo: dateTo}"/>
</div> </div>
</f:else> </f:else>
</f:if> </f:if>
+2 -1
View File
@@ -26,7 +26,8 @@ Encore
babelConfig.plugins = [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-syntax-dynamic-import' ] babelConfig.plugins = [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-syntax-dynamic-import' ]
}) })
.configureWatchOptions(function (options) { .configureWatchOptions(function (options) {
options.poll = 250; options.poll = 500;
options.ignored = /node_modules|vendor|fileadmin/;
}); });
const EpThemeConfig = Encore.getWebpackConfig(); const EpThemeConfig = Encore.getWebpackConfig();