Merge branch 'feature/teaser-for-non-bookable' into develop

This commit is contained in:
Björn Fromme
2020-02-05 12:41:50 +01:00
15 changed files with 84 additions and 27 deletions
@@ -40,6 +40,11 @@ class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implements
*/
protected $bookable = true;
/**
* @var bool
*/
protected $hideBookingButton = false;
/**
* @var bool
*/
@@ -302,6 +307,22 @@ class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implements
$this->bookable = (bool) $bookable;
}
/**
* @return bool
*/
public function getHideBookingButton()
{
return $this->hideBookingButton;
}
/**
* @param bool $hideBookingButton
*/
public function setHideBookingButton($hideBookingButton)
{
$this->hideBookingButton = (bool) $hideBookingButton;
}
/**
* @return bool
*/
@@ -100,7 +100,7 @@ class ProductRepository extends AbstractRepository
'date.board as board', 'date.product_fact as productFact', 'date.hotel_fact as hotelFact',
'date.region_fact as regionFact', 'date.services_included as servicesIncluded',
'date.min_price as minPrice', 'date.nights as nights', 'date.available as available',
'date.pseudo_price as pseudoPrice'
'date.pseudo_price as pseudoPrice', 'date.hide_booking_button as hideBookingButton'
)
->from('tx_epproducts_domain_model_date', 'date')
->leftJoin('date', 'tx_epproducts_domain_model_room', 'room', 'room.date = date.uid')
@@ -154,8 +154,8 @@ class ProductRepository extends AbstractRepository
'date_start as dateStart', 'date_end as dateEnd', 'uid as dateUid', 'min_price as minPrice',
'pseudo_price as pseudoPrice', 'nights as nights', 'available as available',
'skipass_included as skipassIncluded', 'bus_included as busIncluded',
'earlybird', 'new', 'daytrip', 'low_contingent as lowContingent',
'concept_code as conceptCode', 'concept_name as conceptName',
'earlybird', 'new', 'daytrip', 'hide_booking_button as hideBookingButton',
'low_contingent as lowContingent', 'concept_code as conceptCode', 'concept_name as conceptName',
'board'
)
->from('tx_epproducts_domain_model_date', 'date')
@@ -232,7 +232,7 @@ class DateImportService implements SingletonInterface
$dateCount = 0;
$sql = '
SELECT p.uid uid, p.daytrip daytrip, p.important important, p.name product_name,
SELECT p.uid uid, p.daytrip daytrip, p.hide_booking_button, p.important important, p.name product_name,
p.searchable product_searchable, p.detail_page product_detail_page, p.keywords product_keywords,
p.teaser product_teaser, p.feature product_feature, p.subline product_subline, p.country country,
p.region region, p.city city, p.path_segment slug,
@@ -365,6 +365,7 @@ class DateImportService implements SingletonInterface
'bus_pro_id' => $busProId,
'code' => $code,
'daytrip' => $product['daytrip'],
'hide_booking_button' => $product['hide_booking_button'],
'important' => $product['important'],
];
@@ -619,7 +620,7 @@ class DateImportService implements SingletonInterface
foreach ($section as $service)
{
if ($service['price'] < 0) {
return $service['price'];
return (int) $service['price'];
}
}
}
@@ -724,8 +725,8 @@ class DateImportService implements SingletonInterface
foreach ($services['BUS'] as $service)
{
$date['bus_available'] = 1;
$date['bus_price'] = $service['price'];
$date['bus_included'] = ((int) $service['price'] === 0);
$date['bus_price'] = (int) $service['price'];
$date['bus_included'] = (int) ((int) $service['price'] === 0);
}
}
@@ -107,6 +107,7 @@ class DateService implements SingletonInterface
'template' => $template,
'paCode' => $paCode,
'isDayTrip' => $product->isDaytrip(),
'isHideBookingButton' => $product->getHideBookingButton(),
]);
}
@@ -148,6 +149,7 @@ class DateService implements SingletonInterface
'template' => $template,
'paCode' => $paCode,
'isDayTrip' => $product->isDaytrip(),
'isHideBookingButton' => $product->getHideBookingButton(),
]);
}
@@ -259,9 +261,10 @@ class DateService implements SingletonInterface
public function preprocessPriceTable(array $options)
{
$resolver = new OptionsResolver();
$resolver->setDefined('paCode');
$resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton']);
$resolver->setRequired(['priceTableData', 'template']);
$resolver->setDefault('isDayTrip', false);
$resolver->setDefault('isHideBookingButton', false);
$resolvedOptions = $resolver->resolve($options);
$priceTable = [];
@@ -269,6 +272,7 @@ class DateService implements SingletonInterface
$template = $resolvedOptions['template'];
$paCode = $resolvedOptions['paCode'];
$isDayTrip = $resolvedOptions['isDayTrip'];
$isHideBookingButton = $resolvedOptions['isHideBookingButton'];
foreach ($priceTableData as $row)
{
@@ -299,6 +303,7 @@ class DateService implements SingletonInterface
'hotelBusProId' => $row['hotelBusProId'],
'roomBusProId' => $row['roomBusProId'],
'bookingUrl' => $bookingUrl,
'isHideBookingButton' => $isHideBookingButton,
];
}
@@ -312,9 +317,10 @@ class DateService implements SingletonInterface
public function preprocessDateTable(array $options)
{
$resolver = new OptionsResolver();
$resolver->setDefined('paCode');
$resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton']);
$resolver->setRequired(['dateTableData', 'template']);
$resolver->setDefault('isDayTrip', false);
$resolver->setDefault('isHideBookingButton', false);
$resolvedOptions = $resolver->resolve($options);
$dateTable = [];
@@ -323,6 +329,7 @@ class DateService implements SingletonInterface
$template = $resolvedOptions['template'];
$paCode = $resolvedOptions['paCode'];
$isDayTrip = $resolvedOptions['isDayTrip'];
$isHideBookingButton = $resolvedOptions['isHideBookingButton'];
foreach ($dateTableData as $row)
{
@@ -351,6 +358,7 @@ class DateService implements SingletonInterface
'dateServicesGeneral' => unserialize($row['dateServicesGeneral'], ['allowed_classes' => false]),
'available' => $row['available'],
'bookingUrl' => $bookingUrl,
'isHideBookingButton' => $isHideBookingButton,
];
}
@@ -197,6 +197,7 @@ class ProductDataService implements SingletonInterface
'earlybird' => $teaserData['earlybird'],
'new' => $teaserData['new'],
'daytrip' => $teaserData['daytrip'],
'hideBookingButton' => $teaserData['hideBookingButton'],
'lowContingent' => $teaserData['lowContingent'],
'servicesIncluded' => unserialize($teaserData['servicesIncluded']),
'product' => [
@@ -107,8 +107,7 @@ class SearchResultService implements SingletonInterface
// Use compound key, hotel is not unique
$key = $row['hotelUid'] . ':' . $row['productUid'];
$hotelCategory = array_key_exists($row['hotelCategory'], Hotel::$categoryLabels) ?
Hotel::$categoryLabels[$row['hotelCategory']] : null;
$hotelCategory = Hotel::$categoryLabels[$row['hotelCategory']] ?? null;
if (!array_key_exists($key, $items)) {
$items[$key] = [
@@ -119,6 +118,7 @@ class SearchResultService implements SingletonInterface
'detailPageUri' => null,
'earlybird' => $row['earlybird'],
'daytrip' => $row['daytrip'],
'hideBookingButton' => $row['hideBookingButton'],
'lowContingent' => $row['lowContingent'],
'servicesIncluded' => unserialize($row['servicesIncluded']),
'hotel' => [
@@ -28,7 +28,8 @@ return [
concept_description, board_description, additional_services, programme_description, ski_pass_description,
rating_average, rating_votes_count, teaser_images, header_images, images, concept, country, region, city,
journey, facts, officetip, hotels, calendar_hotel, faqs,header_title,header_subtitle, alt_product, alt_label,
video, banner, additional_regions, badge, daytrip, testimonials, external_link, path_segment',
video, banner, additional_regions, badge, daytrip, testimonials, external_link, path_segment,
hide_booking_button',
],
'types' => [
'1' => [
@@ -44,8 +45,8 @@ return [
],
'palettes' => [
'destinations' => ['showitem' => 'country,region,city'],
'top' => ['showitem' => 'searchable, bookable, daytrip, important, skipass_included, detail_page,
external_link, path_segment'],
'top' => ['showitem' => 'searchable, bookable, hide_booking_button, daytrip, important, skipass_included,
detail_page, external_link, path_segment'],
'name' => ['showitem' => 'name,code'],
'alternative' => ['showitem' => 'alt_product, alt_label'],
'header' => ['showitem' => 'headline,subline,--linebreak--,header_title,header_subtitle'],
@@ -155,6 +156,13 @@ return [
'type' => 'check',
],
],
'hide_booking_button' => [
'exclude' => 0,
'label' => 'Buchungsbutton verstecken',
'config' => [
'type' => 'check',
],
],
'daytrip' => [
'exclude' => 0,
'label' => 'tageweise buchbar',
@@ -10,6 +10,7 @@ CREATE TABLE tx_epproducts_domain_model_product
searchable tinyint(4) unsigned DEFAULT '1' NOT NULL,
bookable tinyint(4) unsigned DEFAULT '1' NOT NULL,
hide_booking_button tinyint(4) unsigned DEFAULT '0' NOT NULL,
daytrip tinyint(4) unsigned DEFAULT '0' NOT NULL,
important tinyint(4) unsigned DEFAULT '0' NOT NULL,
skipass_included tinyint(4) unsigned DEFAULT '1' NOT NULL,
@@ -119,6 +120,7 @@ CREATE TABLE tx_epproducts_domain_model_date
available int(11) DEFAULT '0' NOT NULL,
product int(11) unsigned DEFAULT '0',
daytrip tinyint(1) unsigned DEFAULT '0' NOT NULL,
hide_booking_button tinyint(1) unsigned DEFAULT '0' NOT NULL,
important tinyint(1) unsigned DEFAULT '0' NOT NULL,
product_name varchar(255) DEFAULT '' NOT NULL,
product_subline varchar(255) DEFAULT '' NOT NULL,
@@ -36,7 +36,7 @@
<tbody>
<tr v-for="(row, index) of datesRows" :class="{ 'hidden': index > 4 && !showAll }">
<td class="dates-table__cell dates-table__cell--date">
<template v-if="bookable && row.available">
<template v-if="bookable && row.available && !row.isHideBookingButton">
<a class="dates-table__date" href="#"
@click.prevent="loadPriceTable(row.dateUid)">
{{ row.dateStart }} - {{ row.dateEnd }}
@@ -71,9 +71,9 @@
class="hidden-sm hidden-md hidden-lg"><i class="fa fa-info-circle"></i></a>
</td>
<td class="dates-table__cell dates-table__cell--buttons hidden-xs">
<button v-if="row.available" class="button button--small" @click="loadPriceTable(row.dateUid)">Details</button>
<a :href="row.bookingUrl" target="_blank" v-if="row.available" class="button button--small button--action">Buchen</a>
<span v-if="!row.available" class="label label-default">ausgebucht</span>
<button v-if="row.available || row.isHideBookingButton" class="button button--small" @click="loadPriceTable(row.dateUid)">Details</button>
<a :href="row.bookingUrl" target="_blank" v-if="row.available && !row.isHideBookingButton" class="button button--small button--action">Buchen</a>
<span v-if="!row.available && !row.isHideBookingButton" class="label label-default">ausgebucht</span>
</td>
</tr>
</tbody>
@@ -83,7 +83,7 @@
<div class="col-xs-12 col-md-6">
<a href="#" v-if="datesRows.length > 5" @click.prevent="toggleAllDates()">
<i class="fa" :class="[{ 'fa-minus-circle': showAll }, { 'fa-plus-circle': !showAll }]"></i>
Alle Termine <template v-if="showAll">aus</template><template v-else>ein</template>blenden
<template v-if="showAll">Weniger Termine anzeigen</template><template v-else>Alle Termine anzeigen</template>
</a>
</div>
<div class="col-xs-12 col-md-6">
@@ -38,7 +38,7 @@
<td style="white-space: normal">
<a class="pricetable__room-label"
:href="row.bookingUrl" target="_blank">{{ row.roomName }}</a>
<div class="hidden-lg" v-if="row.roomAvailable">
<div class="hidden-lg" v-if="row.roomAvailable && !row.isHideBookingButton">
<a class="button button--small button--action"
:href="row.bookingUrl" target="_blank">Jetzt buchen</a>
</div>
@@ -46,7 +46,7 @@
<span class="label label-default">ausgebucht</span>
</div>
</td>
<td style="white-space: nowrap" :style="{'text-decoration': !row.roomAvailable ? 'line-through' : ''}">{{ calculatePrice(row) }} </td>
<td style="white-space: nowrap" :style="{'text-decoration': !row.roomAvailable && !row.isHideBookingButton ? 'line-through' : ''}">{{ calculatePrice(row) }} </td>
<td>
<i v-if="showBusIcon(row)" data-placement="top" title="Busfahrt inklusive" class="fa fa-bus tooltiptoggle"></i>
<i v-if="row.dateSkipassIncluded" data-placement="top" title="Skipass inklusive" class="fa fa-tag tooltiptoggle"></i>
@@ -75,9 +75,9 @@
</div>
</td>
<td class="hidden-xs hidden-md">
<a class="button button--small button--action" v-if="row.roomAvailable"
<a class="button button--small button--action" v-if="row.roomAvailable && !row.isHideBookingButton"
:href="row.bookingUrl" target="_blank">Buchen</a>
<span v-if="!row.roomAvailable" class="label label-default">ausgebucht</span>
<span v-if="!row.roomAvailable && !row.isHideBookingButton" class="label label-default">ausgebucht</span>
</td>
</tr>
</tbody>
@@ -56,8 +56,8 @@
<div class="teaserbox__aside__bottom">
<span class="searchresult-item__price">ab {{ item.minPrice }} </span>
<a :href="url" class="button button--full searchresult-item__button"
rel="nofollow" title="Details &amp; Buchen">
Details &amp; Buchen
rel="nofollow" title="Details">
Details<template v-if="!item.hideBookingButton"> &amp; Buchen</template>
</a>
</div>
</div>
@@ -41,6 +41,9 @@
<trans-unit id="tx_eptheme.label.teaserbutton.product">
<target>Details &amp; Buchen</target>
</trans-unit>
<trans-unit id="tx_eptheme.label.teaserbutton.product_no_booking">
<target>Details</target>
</trans-unit>
<trans-unit id="tx_eptheme_domain_model_lineup">
<target>Lineup</target>
</trans-unit>
@@ -40,6 +40,9 @@
<trans-unit id="tx_eptheme.label.teaserbutton.product">
<source>Details &amp; Buchen</source>
</trans-unit>
<trans-unit id="tx_eptheme.label.teaserbutton.product_no_booking">
<source>Details</source>
</trans-unit>
<trans-unit id="tx_eptheme_domain_model_lineup">
<source>Lineup</source>
</trans-unit>
@@ -86,7 +86,12 @@
<div class="teaserbox__back">
{teaser.product.teaser -> f:format.html()}
</div>
<button class="button button--full teaserbox__button" title="{teaser.product.name}">{f:translate(key: 'tx_eptheme.label.teaserbutton.product', extensionName: 'ep_theme')}</button>
<button class="button button--full teaserbox__button" title="{teaser.product.name}">
<f:if condition="{teaser.hideBookingButton}">
<f:then>{f:translate(key: 'tx_eptheme.label.teaserbutton.product', extensionName: 'ep_theme')}</f:then>
<f:else>{f:translate(key: 'tx_eptheme.label.teaserbutton.product_no_booking', extensionName: 'ep_theme')}</f:else>
</f:if>
</button>
</div>
</f:section>
@@ -94,7 +94,12 @@
</div>
<div class="teaserbox__aside__bottom">
<span class="searchresult-item__price">ab {teaser.minPrice} €{f:if(condition: teaser.pseudoPrice, then: ' <s class="searchresult-item__pseudo-price">{teaser.pseudoPrice} €</s>')}</span>
<a href="{teaserUrl}" title="{teaser.product.name}" class="button button--full searchresult-item__button">Details &amp; Buchen</a>
<a href="{teaserUrl}" title="{teaser.product.name}" class="button button--full searchresult-item__button">
<f:if condition="{teaser.hideBookingButton}">
<f:then>{f:translate(key: 'tx_eptheme.label.teaserbutton.product', extensionName: 'ep_theme')}</f:then>
<f:else>{f:translate(key: 'tx_eptheme.label.teaserbutton.product_no_booking', extensionName: 'ep_theme')}</f:else>
</f:if>
</a>
</div>
</div>
<watchlist-toggle uid="{teaser.key}" class="teaserbox-watchlist"></watchlist-toggle>