diff --git a/composer.json b/composer.json
index fcfe37f8..a1c3e8a9 100644
--- a/composer.json
+++ b/composer.json
@@ -61,13 +61,13 @@
"typo3-ter/sourceopt": "^1.0",
"typo3-ter/html-compress": "^1.1",
"typo3-ter/imageoptimizer": "^1.0",
- "typo3-ter/loginlimit": "^1.0",
"doctrine/dbal": "^2.5",
"yohang/calendr": "^2.1",
"wyrihaximus/html-compress": "^1.4",
"symfony/property-access": "^3.4",
"symfony/options-resolver": "^3.4",
- "symfony/serializer": "^3.3"
+ "symfony/serializer": "^3.3",
+ "league/csv": "^8.2"
},
"scripts": {
"typo3-cms-scripts": [
diff --git a/composer.lock b/composer.lock
index 2c741187..4f9a6338 100644
--- a/composer.lock
+++ b/composer.lock
@@ -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": "f02741f6bee387f8f2b8ec67b91ef940",
+ "content-hash": "b82dc74c08630a468c17439fc8b67cb3",
"packages": [
{
"name": "cogpowered/finediff",
@@ -939,6 +939,63 @@
],
"time": "2014-06-03T12:02:11+00:00"
},
+ {
+ "name": "league/csv",
+ "version": "8.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/csv.git",
+ "reference": "d2aab1e7bde802582c3879acf03d92716577c76d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/csv/zipball/d2aab1e7bde802582c3879acf03d92716577c76d",
+ "reference": "d2aab1e7bde802582c3879acf03d92716577c76d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^1.9",
+ "phpunit/phpunit": "^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Csv\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://github.com/nyamsprod/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Csv data manipulation made easy in PHP",
+ "homepage": "http://csv.thephpleague.com",
+ "keywords": [
+ "csv",
+ "export",
+ "filter",
+ "import",
+ "read",
+ "write"
+ ],
+ "time": "2018-02-06T08:27:03+00:00"
+ },
{
"name": "linkorb/jsmin-php",
"version": "1.0.0",
@@ -2421,7 +2478,6 @@
}
],
"description": "This extension integrates the grid layout concept also to regular content elements - the grid elements.\r\nIt offers a lot of new features like advanced drag & drop or real references, that improve the usability of the page and list module to speed up the daily work with the backend.",
- "homepage": "https://extensions.typo3.org/extension/gridelements/",
"time": "2017-12-23T18:15:17+00:00"
},
{
@@ -2455,7 +2511,6 @@
}
],
"description": "Compress/minify your HTML output",
- "homepage": "https://extensions.typo3.org/extension/html_compress/",
"time": "2016-12-26T10:19:02+00:00"
},
{
@@ -2492,39 +2547,6 @@
"abandoned": "christophlehmann/imageoptimizer",
"time": "2018-08-20T23:03:56+00:00"
},
- {
- "name": "typo3-ter/loginlimit",
- "version": "1.0.3",
- "dist": {
- "type": "zip",
- "url": "https://extensions.typo3.org/extension/download/loginlimit/1.0.3/zip/",
- "reference": null,
- "shasum": null
- },
- "require": {
- "typo3/cms-core": ">= 6.2.0, <= 8.7.99"
- },
- "replace": {
- "loginlimit": "self.version"
- },
- "type": "typo3-cms-extension",
- "autoload": {
- "psr-4": {
- "WebentwicklerAt\\Loginlimit\\": "Classes/"
- }
- },
- "authors": [
- {
- "name": "Gernot Leitgab",
- "email": "",
- "company": "Webentwickler.at",
- "username": "royalknight"
- }
- ],
- "description": "Protect backend and/or frontend login against brute-force attacks.",
- "homepage": "https://extensions.typo3.org/extension/loginlimit/",
- "time": "2017-09-23T10:40:00+00:00"
- },
{
"name": "typo3-ter/media",
"version": "4.5.0",
@@ -2559,7 +2581,6 @@
}
],
"description": "Media management system for TYPO3 CMS.",
- "homepage": "https://extensions.typo3.org/extension/media/",
"time": "2017-11-02T13:08:04+00:00"
},
{
@@ -2593,7 +2614,6 @@
}
],
"description": "Search Engine Optimization (SEO), Indexed Google-Sitemap (TXT- and XML-Sitemap) for all Extensions (pibase, extbase), Metatags, Canonical-URL, Pagetitle manipulations, Crawler verification, Piwik and Google Analytics support and some more... multi-language- and multi-tree-support",
- "homepage": "https://extensions.typo3.org/extension/metaseo/",
"time": "2017-06-11T18:32:55+00:00"
},
{
@@ -2633,7 +2653,6 @@
}
],
"description": "Simply manage (permanent 301) redirects in the list view. The extension supports multidomain sites, count the redirect usage and shows the last referer. UPDATE! imports the records from realurl.",
- "homepage": "https://extensions.typo3.org/extension/ods_redirects/",
"time": "2017-05-22T22:07:45+00:00"
},
{
@@ -2667,7 +2686,6 @@
}
],
"description": "Provides API for Backend modules to get a proper path to the Frontend page (simulateStatic/RealURL/CoolURI-like)",
- "homepage": "https://extensions.typo3.org/extension/pagepath/",
"time": "2017-02-15T15:40:25+00:00"
},
{
@@ -2704,7 +2722,6 @@
"username": "plusb"
}
],
- "homepage": "https://extensions.typo3.org/extension/pb_social/",
"time": "2018-02-14T11:05:37+00:00"
},
{
@@ -2738,7 +2755,6 @@
}
],
"description": "Optimization of the final page: reformatting the (x)html output, removal of new lines, and quotes. Move development repository to https://github.com/lochmueller/sourceopt",
- "homepage": "https://extensions.typo3.org/extension/sourceopt/",
"time": "2017-06-15T17:25:53+00:00"
},
{
diff --git a/web/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php b/web/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php
new file mode 100644
index 00000000..d97099eb
--- /dev/null
+++ b/web/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php
@@ -0,0 +1,117 @@
+, dreipunktnull
+ *
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use EP\EpProducts\Domain\Repository\ProductRepository;
+use EP\EpProducts\Service\FilterService;
+use EP\EpProducts\Service\SearchResultService;
+use EP\EpProducts\Service\SearchResultUrlService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+
+class AjaxWatchlistController extends ActionController
+{
+
+ /**
+ * @var ProductRepository
+ */
+ protected $productRepository;
+
+ /**
+ * @var SearchResultService
+ */
+ protected $searchResultService;
+
+ /**
+ * @var SearchResultUrlService
+ */
+ protected $searchResultUrlService;
+
+ /**
+ * @var FilterService
+ */
+ protected $filterService;
+
+ /**
+ * @param ProductRepository $productRepository
+ * @param SearchResultService $searchResultService
+ * @param SearchResultUrlService $searchResultUrlService
+ * @param FilterService $filterService
+ */
+ public function __construct
+ (
+ ProductRepository $productRepository,
+ SearchResultService $searchResultService,
+ SearchResultUrlService $searchResultUrlService,
+ FilterService $filterService
+ )
+ {
+ parent::__construct();
+
+ $this->productRepository = $productRepository;
+ $this->searchResultService = $searchResultService;
+ $this->searchResultUrlService = $searchResultUrlService;
+ $this->filterService = $filterService;
+ }
+
+ /**
+ * @param array $watchlistUids
+ * @param string $referringPageUid
+ * @return string
+ */
+ public function listAction(array $watchlistUids, $referringPageUid)
+ {
+ $productUids = array_map(function ($uid) {
+ list (, $productUid) = GeneralUtility::trimExplode(':', $uid);
+ return $productUid;
+ }, $watchlistUids);
+
+ $hotelUids = array_map(function ($uid) {
+ list ($hotelUid,) = GeneralUtility::trimExplode(':', $uid);
+ return $hotelUid;
+ }, $watchlistUids);
+
+ $watchlistData = $this->productRepository->getWatchlist($productUids, $hotelUids);
+ $watchlist = $this->searchResultService->preprocess($watchlistData);
+
+ $forcedConceptUid = (int) $this->settings['forcedConceptUid'];
+ $filterSettings = $this->filterService->getDefaultFilterSettings($forcedConceptUid);
+
+ $options = [
+ 'defaultDetailPageUid' => $this->settings['defaultDetailPageUid'],
+ 'referringPageUid' => $referringPageUid,
+ 'filterSettings' => $filterSettings,
+ ];
+ $this->searchResultUrlService->process($watchlist, $options);
+
+ return json_encode([
+ 'data' => $watchlist->getData(),
+ 'total' => $watchlist->getTotal()
+ ]);
+ }
+}
diff --git a/web/typo3conf/ext/ep_products/Classes/Controller/WatchlistController.php b/web/typo3conf/ext/ep_products/Classes/Controller/WatchlistController.php
new file mode 100644
index 00000000..86a68502
--- /dev/null
+++ b/web/typo3conf/ext/ep_products/Classes/Controller/WatchlistController.php
@@ -0,0 +1,40 @@
+, dreipunktnull
+ *
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+
+class WatchlistController extends ActionController
+{
+
+ public function listAction()
+ {
+ $this->view->assign('referringPageUid', $GLOBALS['TSFE']->id);
+ }
+
+}
diff --git a/web/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php b/web/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php
index d142dd2e..43adb673 100644
--- a/web/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php
+++ b/web/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php
@@ -36,6 +36,44 @@ use EP\EpProducts\Utility\DbUtility;
class ProductRepository extends AbstractRepository
{
+ /**
+ * @param array $productUids
+ * @param array $hotelUids
+ * @return array
+ */
+ public function getWatchlist(array $productUids, array $hotelUids)
+ {
+ $qb = DbUtility::getDbConnection()->createQueryBuilder();
+
+ $query = $qb
+ ->select(
+ 'date.concept conceptUid', 'date.concept_name conceptName', 'date.concept_code conceptCode',
+ 'date.product productUid', 'date.product_name productName', 'date.product_detail_page productDetailPage',
+ 'date.product_feature productFeature',
+ 'date.country countryUid','date.country_name countryName', 'date.country_code countryCode',
+ 'date.region regionUid', 'date.region_name regionName', 'date.region_feature regionFeature',
+ 'date.date_start dateStart', 'date.date_end dateEnd',
+ 'date.hotel hotelUid', 'date.hotel_name hotelName', 'date.hotel_category hotelCategory',
+ 'date.earlybird earlybird', 'date.low_contingent lowContingent', 'date.board board',
+ 'date.product_fact productFact', 'date.hotel_fact hotelFact', 'date.region_fact regionFact',
+ 'date.services_included servicesIncluded', 'date.min_price minPrice', 'date.nights nights',
+ 'date.available available', 'date.pseudo_price pseudoPrice'
+ )
+ ->from('tx_epproducts_domain_model_date', 'date')
+ ->where('date.product IN (:productUids)')
+ ->andWhere('date.hotel IN (:hotelUids)')
+ ->andWhere('date.product_searchable = 1')
+ ->orderBy('date.concept_sorting')
+ ->addOrderBy('date.product_name')
+ ->addOrderBy('date.min_price')
+ ->addOrderBy('date.date_start')
+ ->addOrderBy('date.hotel_name')
+ ->setParameter('productUids', $productUids, Connection::PARAM_INT_ARRAY)
+ ->setParameter('hotelUids', $hotelUids, Connection::PARAM_INT_ARRAY)
+ ;
+
+ return $query->execute()->fetchAll();
+ }
/**
* @param FilterSettings $filterSettings
diff --git a/web/typo3conf/ext/ep_products/Classes/Service/ProductService.php b/web/typo3conf/ext/ep_products/Classes/Service/ProductService.php
index 1d02417f..5d96d669 100644
--- a/web/typo3conf/ext/ep_products/Classes/Service/ProductService.php
+++ b/web/typo3conf/ext/ep_products/Classes/Service/ProductService.php
@@ -183,7 +183,11 @@ class ProductService implements SingletonInterface
$hotelCategory = array_key_exists($teaserData['hotelCategory'], Hotel::$categoryLabels) ?
Hotel::$categoryLabels[$teaserData['hotelCategory']] : null;
+ // Use compound key, hotel is not unique
+ $key = $teaserData['hotelUid'] . ':' . $teaserData['productUid'];
+
return [
+ 'key' => $key,
'minDateStart' => null,
'maxDateEnd' => null,
'minPrice' => null,
diff --git a/web/typo3conf/ext/ep_products/Configuration/TypoScript/setup.txt b/web/typo3conf/ext/ep_products/Configuration/TypoScript/setup.txt
index 2b12e567..fa9d1ad9 100644
--- a/web/typo3conf/ext/ep_products/Configuration/TypoScript/setup.txt
+++ b/web/typo3conf/ext/ep_products/Configuration/TypoScript/setup.txt
@@ -127,6 +127,9 @@ tx_epproducts_ajax_json {
AjaxCalendar {
1 = range
}
+ AjaxWatchlist {
+ 1 = list
+ }
}
settings =< plugin.tx_epproducts.settings
diff --git a/web/typo3conf/ext/ep_products/ext_localconf.php b/web/typo3conf/ext/ep_products/ext_localconf.php
index 0b589847..d23296b2 100644
--- a/web/typo3conf/ext/ep_products/ext_localconf.php
+++ b/web/typo3conf/ext/ep_products/ext_localconf.php
@@ -308,6 +308,17 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
]
);
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
+ 'EP.' . $_EXTKEY,
+ 'watchlist',
+ [
+ 'Watchlist' => 'list',
+ ],
+ [
+ 'Watchlist' => '',
+ ]
+);
+
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'EP.' . $_EXTKEY,
'ajax',
@@ -319,6 +330,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
'AjaxContent' => 'country,region,city,hotel,product,journey,webcam',
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
'AjaxCalendar' => 'range',
+ 'AjaxWatchlist' => 'list',
],
[
'AjaxSearch' => 'searchresult',
@@ -328,6 +340,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
'AjaxContent' => 'webcam',
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
'AjaxCalendar' => 'range',
+ 'AjaxWatchlist' => 'list',
]
);
diff --git a/web/typo3conf/ext/ep_products/ext_tables.php b/web/typo3conf/ext/ep_products/ext_tables.php
index bc95113a..dd0b78c4 100644
--- a/web/typo3conf/ext/ep_products/ext_tables.php
+++ b/web/typo3conf/ext/ep_products/ext_tables.php
@@ -143,6 +143,12 @@ if (!defined('TYPO3_MODE')) {
'Vertriebspartner Export'
);
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
+ 'EP.' . $_EXTKEY,
+ 'watchlist',
+ 'Merkliste'
+);
+
$extensionName = strtolower(\TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($_EXTKEY));
$pluginSignature = str_replace('_', '', $_EXTKEY) . '_product_detail';
diff --git a/web/typo3conf/ext/ep_theme/Configuration/TypoScript/constants.txt b/web/typo3conf/ext/ep_theme/Configuration/TypoScript/constants.txt
index 6f570e34..6bcb5cb2 100644
--- a/web/typo3conf/ext/ep_theme/Configuration/TypoScript/constants.txt
+++ b/web/typo3conf/ext/ep_theme/Configuration/TypoScript/constants.txt
@@ -43,6 +43,8 @@ plugin.tx_eptheme {
groupsContactFormPageUid =
# cat=eptheme/200/250; type=string; label=Rating page UID
ratingPageUid =
+ # cat=eptheme/200/260; type=string; label=Watchlist page UID
+ watchlistPageUid =
# cat=eptheme/400/100; type=string; label=Phone number general
phoneNumber = 0221 - 272 276 0
diff --git a/web/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.txt b/web/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.txt
index 6c7705cf..a38b6190 100644
--- a/web/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.txt
+++ b/web/typo3conf/ext/ep_theme/Configuration/TypoScript/setup.txt
@@ -81,6 +81,7 @@ plugin.tx_eptheme {
groupsContactFormPageUid = {$plugin.tx_eptheme.settings.groupsContactFormPageUid}
blogDetailPageUid = {$plugin.tx_eptheme.settings.blogDetailPageUid}
ratingPageUid = {$plugin.tx_eptheme.settings.ratingPageUid}
+ watchlistPageUid = {$plugin.tx_eptheme.settings.watchlistPageUid}
}
view {
templateRootPaths.0 = EXT:ep_theme/Resources/Private/Templates
diff --git a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/app.js b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/app.js
index 0415a5f3..bda2736d 100644
--- a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/app.js
+++ b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/app.js
@@ -25,6 +25,7 @@ require('../images/favicon_uch.ico');
// Import dependencies
import Vue from 'vue'
+import VueSession from 'vue-session'
import $ from 'jquery'
import dayjs from 'dayjs'
import 'dayjs/locale/de'
@@ -49,6 +50,8 @@ import Gmap from './components/Gmap.vue'
import Calendar from './components/Calendar.vue'
import EventPricetable from './components/EventPriceTable.vue'
import ContactForm from './components/ContactForm.vue'
+import WatchlistToggle from './components/WatchlistToggle.vue'
+import Watchlist from './components/Watchlist.vue'
// Define global constants
const $window = $(window);
@@ -56,6 +59,9 @@ const sliderPrevArrow = '';
const sliderDot = '';
+// Enable session handling
+Vue.use(VueSession);
+
// Define date filter for vue templates
Vue.filter('date', (value) => {
return dayjs(value).format('DD.MM.YYYY');
@@ -73,7 +79,9 @@ new Vue({
Gmap,
Calendar,
EventPricetable,
- ContactForm
+ ContactForm,
+ WatchlistToggle,
+ Watchlist
},
data () {
return {
@@ -85,6 +93,12 @@ new Vue({
searchbarFixed: false,
mobileMode: false,
backgroundImageMode: 'mobile',
+ watchlist: []
+ }
+ },
+ computed: {
+ watchlistCount () {
+ return this.watchlist.length;
}
},
methods: {
@@ -366,6 +380,25 @@ new Vue({
EventBus.$on('scrollTo', (anchorId, offset) => {
this.scrollTo(anchorId, offset);
});
+ EventBus.$on('watchlistToggle', uid => {
+ let list = this.watchlist;
+ let watchlistIndex = list.indexOf(uid);
+ if (watchlistIndex === -1) {
+ list.push(uid)
+ } else {
+ list.splice(watchlistIndex, 1)
+ }
+ this.watchlist = list;
+ this.$session.set('watchlist', this.watchlist);
+
+ });
+ if (!this.$session.exists()) {
+ this.$session.start();
+ }
+ if (!this.$session.has('watchlist')) {
+ this.$session.set('watchlist', []);
+ }
+ this.watchlist = this.$session.get('watchlist');
},
mounted () {
Vue.nextTick(() => {
diff --git a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResult.vue b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResult.vue
index dffff35a..604666e8 100644
--- a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResult.vue
+++ b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResult.vue
@@ -73,6 +73,12 @@
default () {
return {};
}
+ },
+ watchlist: {
+ type: Array,
+ default () {
+ return [];
+ }
}
},
computed: {
@@ -133,7 +139,6 @@
this.loading = false;
EventBus.$emit('ajaxContentLoaded');
});
-
},
resetFilterSettings () {
this.filterSettings = this.defaultFilterSettings;
diff --git a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResultItem.vue b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResultItem.vue
index df915d9e..a83a39c3 100644
--- a/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResultItem.vue
+++ b/web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/SearchResultItem.vue
@@ -59,14 +59,19 @@
+