From c11942bc7a2dcd35812f9738cc8578a1b10fee87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Fromme?= Date: Tue, 21 Aug 2018 21:01:37 +0200 Subject: [PATCH] Implement watchlist --- composer.json | 4 +- composer.lock | 100 ++++++++------- .../Controller/AjaxWatchlistController.php | 117 ++++++++++++++++++ .../Controller/WatchlistController.php | 40 ++++++ .../Domain/Repository/ProductRepository.php | 38 ++++++ .../Classes/Service/ProductService.php | 4 + .../Configuration/TypoScript/setup.txt | 3 + .../ext/ep_products/ext_localconf.php | 13 ++ web/typo3conf/ext/ep_products/ext_tables.php | 6 + .../Configuration/TypoScript/constants.txt | 2 + .../Configuration/TypoScript/setup.txt | 1 + .../Resources/Private/Assets/js/app.js | 35 +++++- .../Assets/js/components/SearchResult.vue | 7 +- .../Assets/js/components/SearchResultItem.vue | 11 ++ .../Assets/js/components/Watchlist.vue | 88 +++++++++++++ .../Assets/js/components/WatchlistToggle.vue | 37 ++++++ .../Resources/Private/Assets/scss/main.scss | 2 +- .../Private/Assets/scss/main_boarderweek.scss | 1 + .../Private/Assets/scss/main_snowzone.scss | 1 + .../Private/Assets/scss/main_unichamp.scss | 1 + .../Assets/scss/remix/_contactnav.scss | 3 + .../Private/Assets/scss/remix/_product.scss | 12 ++ .../Private/Assets/scss/remix/_teaserbox.scss | 40 +++++- .../Private/Assets/scss/remix/_watchlist.scss | 21 ++++ .../Private/Partials/ContactBar.html | 4 + .../Partials/Product/HeaderDetail.html | 16 ++- .../Private/Partials/Product/TeaserWide.html | 1 + .../Templates/FluidStyledContent/List.html | 7 ++ .../Templates/Search/Searchresult.html | 12 +- .../Private/Templates/Watchlist/List.html | 14 +++ 30 files changed, 583 insertions(+), 58 deletions(-) create mode 100644 web/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php create mode 100644 web/typo3conf/ext/ep_products/Classes/Controller/WatchlistController.php create mode 100644 web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/Watchlist.vue create mode 100644 web/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/WatchlistToggle.vue create mode 100644 web/typo3conf/ext/ep_theme/Resources/Private/Assets/scss/remix/_watchlist.scss create mode 100644 web/typo3conf/ext/ep_theme/Resources/Private/Templates/Watchlist/List.html 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 = '