Merge branch 'develop' into feature/typo3-10

This commit is contained in:
Björn Fromme
2021-09-06 14:33:11 +02:00
81 changed files with 1443 additions and 917 deletions
+46 -25
View File
@@ -1,8 +1,8 @@
name: ep-reisen
type: typo3
docroot: public
php_version: "7.3"
webserver_type: apache-fpm
php_version: "7.4"
webserver_type: nginx-fpm
router_http_port: "80"
router_https_port: "443"
xdebug_enabled: false
@@ -14,23 +14,26 @@ additional_hostnames:
- ski-wochenenden
- schnee-event
additional_fqdns: []
mariadb_version: "10.2"
mysql_version: ""
nfs_mount_enabled: true
provider: default
omit_containers: [dba]
use_dns_when_possible: true
timezone: Europe/Berlin
webimage_extra_packages: [ ssh ]
hooks:
post-start:
- exec: "sudo chmod 600 /home/fromme/.ssh/authorized_keys"
- exec: "sudo service ssh start"
- exec: sudo chmod 600 /home/fromme/.ssh/authorized_keys
- exec: sudo service ssh start
omit_containers: [dba]
webimage_extra_packages: [ssh]
use_dns_when_possible: true
timezone: Europe/Berlin
composer_version: "2"
web_environment:
- TYPO3_CONTEXT=Development
# This config.yaml was created with ddev version v1.14.0
# webimage: drud/ddev-webserver:v1.14.0
# dbimage: drud/ddev-dbserver-mariadb-10.2:v1.14.0
# dbaimage: phpmyadmin/phpmyadmin:5
# This config.yaml was created with ddev version v1.17.7
# webimage: drud/ddev-webserver:v1.17.7
# dbimage: drud/ddev-dbserver-mariadb-10.3:v1.17.7
# dbaimage: phpmyadmin:5
# However we do not recommend explicitly wiring these images into the
# config.yaml as they may break future versions of ddev.
# You can update this config.yaml using 'ddev config'.
@@ -44,7 +47,7 @@ hooks:
# docroot: <relative_path> # Relative path to the directory containing index.php.
# php_version: "7.3" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4"
# php_version: "7.4" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4" "8.0"
# You can explicitly specify the webimage, dbimage, dbaimage lines but this
# is not recommended, as the images are often closely tied to ddev's' behavior,
@@ -65,10 +68,15 @@ hooks:
# xdebug_enabled: false # Set to true to enable xdebug and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev exec enable_xdebug" and "ddev exec disable_xdebug" work better,
# "ddev xdebug" to enable xdebug and "ddev xdebug off" to disable it work better,
# as leaving xdebug enabled all the time is a big performance hit.
# webserver_type: nginx-fpm # Can be set to apache-fpm or apache-cgi as well
# xhprof_enabled: false # Set to true to enable xhprof and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev xhprof" to enable xhprof and "ddev xhprof off" to disable it work better,
# as leaving xhprof enabled all the time is a big performance hit.
# webserver_type: nginx-fpm # or apache-fpm
# timezone: Europe/Berlin
# This is the timezone used in the containers and by PHP;
@@ -76,6 +84,13 @@ hooks:
# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# For example Europe/Dublin or MST7MDT
# composer_version: ""
# if composer_version:"" it will use the current ddev default composer release.
# It can also be set to "1", to get most recent composer v1
# or "2" for most recent composer v2.
# It can be set to any existing specific composer version.
# After first project 'ddev start' this will not be updated until it changes
# additional_hostnames:
# - somename
# - someothername
@@ -98,7 +113,7 @@ hooks:
# These values specify the destination directory for ddev ssh and the
# directory in which commands passed into ddev exec are run.
# omit_containers: ["db", dba", "ddev-ssh-agent"]
# omit_containers: [db, dba, ddev-ssh-agent]
# Currently only these containers are supported. Some containers can also be
# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit
# the "db" container, several standard features of ddev that access the
@@ -108,6 +123,9 @@ hooks:
# Great performance improvement but requires host configuration first.
# See https://ddev.readthedocs.io/en/stable/users/performance/#using-nfs-to-mount-the-project-into-the-container
# fail_on_hook_fail: False
# Decide whether 'ddev start' should be interrupted by a failing hook
# host_https_port: "59002"
# The host port binding for https can be explicitly specified. It is
# dynamic unless otherwise specified.
@@ -125,14 +143,14 @@ hooks:
# unless explicitly specified.
# phpmyadmin_port: "8036"
# phpmyadmin_https_port: "8037{"
# phpmyadmin_https_port: "8037"
# The PHPMyAdmin ports can be changed from the default 8036 and 8037
# mailhog_port: "8025"
# mailhog_https_port: "8026"
# The MailHog ports can be changed from the default 8025 and 8026
# webimage_extra_packages: [php-yaml, php7.3-ldap]
# webimage_extra_packages: [php7.4-tidy, php-bcmath]
# Extra Debian packages that are needed in the webimage can be added here
# dbimage_extra_packages: [telnet,netcat]
@@ -156,17 +174,20 @@ hooks:
# disable_settings_management: false
# If true, ddev will not create CMS-specific settings files like
# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalSettings.php
# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php
# In this case the user must provide all such settings.
# You can inject environment variables into the web container with:
# web_environment:
# - SOMEENV=somevalue
# - SOMEOTHERENV=someothervalue
# no_project_mount: false
# (Experimental) If true, ddev will not mount the project into the web container;
# (Experimental) If true, ddev will not mount the project into the web container;
# the user is responsible for mounting it manually or via a script.
# This is to enable experimentation with alternate file mounting strategies.
# This is to enable experimentation with alternate file mounting strategies.
# For advanced users only!
# provider: default # Currently either "default" or "pantheon"
#
# Many ddev commands can be extended to run tasks before or after the
# ddev command is executed, for example "post-start", "post-import-db",
# "pre-composer", "post-composer"
-2
View File
@@ -2,8 +2,6 @@ version: '3.6'
services:
web:
environment:
- TYPO3_CONTEXT=Development
volumes:
- ../public/typo3conf/ext/ep_theme/Resources/Private/Assets:/var/www/html/public/typo3conf/ext/ep_theme/Resources/Private/Assets:consistent
ports:
+1 -1
View File
@@ -4,7 +4,7 @@ trim_trailing_whitespace = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{html,js}]
[*.{html,js,vue}]
indent_size = 4
indent_style = space
[*.{css,scss}]
+304 -14
View File
@@ -1,4 +1,118 @@
SetEnv TYPO3_CONTEXT "Production"
Deny from 131.221.187.131
Deny from 47.254.122.100
#<mittwald 503 for bots>
###########503-for-Bots##########################
#################################################
#SetEnvIfNoCase User-Agent "Googlebot" bots
#SetEnvIfNoCase User-Agent "Googlebot-Image" bots
#SetEnvIfNoCase User-Agent "Googlebot-Mobile" bots
#SetEnvIfNoCase User-Agent "Java" bots
#SetEnvIfNoCase User-Agent "UptimeRobot" bots
#SetEnvIfNoCase User-Agent "bingbot" bots
#SetEnvIfNoCase User-Agent "msnbot" bots
#SetEnvIfNoCase User-Agent "msnbot-media" bots
SetEnvIfNoCase User-Agent "AACrawler" bots
SetEnvIfNoCase User-Agent "AcoonBot" bots
SetEnvIfNoCase User-Agent "AhrefsBot" bots
SetEnvIfNoCase User-Agent "BLEXBot" bots
SetEnvIfNoCase User-Agent "BUbiNG" bots
SetEnvIfNoCase User-Agent "Baiduspider" bots
SetEnvIfNoCase User-Agent "Baiduspider-image" bots
SetEnvIfNoCase User-Agent "CCBot" bots
SetEnvIfNoCase User-Agent "CaBot" bots
SetEnvIfNoCase User-Agent "CompSpyBot" bots
SetEnvIfNoCase User-Agent "Daum" bots
SetEnvIfNoCase User-Agent "DoCoMo" bots
SetEnvIfNoCase User-Agent "DotBot" bots
SetEnvIfNoCase User-Agent "Exabot" bots
SetEnvIfNoCase User-Agent "Ezooms" bots
SetEnvIfNoCase User-Agent "Faraday" bots
SetEnvIfNoCase User-Agent "FeedBurner" bots
SetEnvIfNoCase User-Agent "HuaweiSymantecSpider" bots
SetEnvIfNoCase User-Agent "Infohelfer" bots
SetEnvIfNoCase User-Agent "JobboerseBot" bots
SetEnvIfNoCase User-Agent "Jooblebot" bots
SetEnvIfNoCase User-Agent "Landau-Media-Spider" bots
SetEnvIfNoCase User-Agent "MJ12bot" bots
SetEnvIfNoCase User-Agent "Mail.RU_Bot" bots
SetEnvIfNoCase User-Agent "MegaIndex" bots
SetEnvIfNoCase User-Agent "MegaIndex.ru" bots
SetEnvIfNoCase User-Agent "MetaJobBot" bots
SetEnvIfNoCase User-Agent "MetricsBot" bots
SetEnvIfNoCase User-Agent "MojeekBot" bots
SetEnvIfNoCase User-Agent "Nutch-1.10" bots
SetEnvIfNoCase User-Agent "OnPageBot" bots
SetEnvIfNoCase User-Agent "OpenWebSpider" bots
SetEnvIfNoCase User-Agent "PRTGCloudBot" bots
SetEnvIfNoCase User-Agent "PiplBot" bots
SetEnvIfNoCase User-Agent "Pixray-Seeker" bots
SetEnvIfNoCase User-Agent "Qwantify" bots
SetEnvIfNoCase User-Agent "Ralocobot" bots
SetEnvIfNoCase User-Agent "SEOkicks" bots
SetEnvIfNoCase User-Agent "SEOkicks-Robot" bots
SetEnvIfNoCase User-Agent "Screaming" bots
SetEnvIfNoCase User-Agent "Seekport" bots
SetEnvIfNoCase User-Agent "SemrushBot" bots
SetEnvIfNoCase User-Agent "SeznamBot" bots
SetEnvIfNoCase User-Agent "Site24x7" bots
SetEnvIfNoCase User-Agent "Slurp" bots
SetEnvIfNoCase User-Agent "Sosospider" bots
SetEnvIfNoCase User-Agent "Speedy" bots
SetEnvIfNoCase User-Agent "Spider" bots
SetEnvIfNoCase User-Agent "Spiderlytics" bots
SetEnvIfNoCase User-Agent "Superfeedr" bots
SetEnvIfNoCase User-Agent "Toweya.com" bots
SetEnvIfNoCase User-Agent "TurnitinBot" bots
SetEnvIfNoCase User-Agent "TwengaBot" bots
SetEnvIfNoCase User-Agent "Twiceler" bots
SetEnvIfNoCase User-Agent "Twitterbot" bots
SetEnvIfNoCase User-Agent "UnisterBot" bots
SetEnvIfNoCase User-Agent "WebCapture" bots
SetEnvIfNoCase User-Agent "XoviBot" bots
SetEnvIfNoCase User-Agent "Yandex" bots
SetEnvIfNoCase User-Agent "ZumBot" bots
SetEnvIfNoCase User-Agent "adscanner" bots
SetEnvIfNoCase User-Agent "aggregator:Spinn3r" bots
SetEnvIfNoCase User-Agent "crawler" bots
SetEnvIfNoCase User-Agent "[email protected]" bots
SetEnvIfNoCase User-Agent "domaincrawler.com" bots
SetEnvIfNoCase User-Agent "echobot" bots
SetEnvIfNoCase User-Agent "heritrix" bots
SetEnvIfNoCase User-Agent "htdig" bots
SetEnvIfNoCase User-Agent "ia_archiver" bots
SetEnvIfNoCase User-Agent "imaBot" bots
SetEnvIfNoCase User-Agent "infometrics-bot" bots
SetEnvIfNoCase User-Agent "jobs.de-Robot" bots
SetEnvIfNoCase User-Agent "linkdex.com" bots
SetEnvIfNoCase User-Agent "magpie-crawler" bots
SetEnvIfNoCase User-Agent "meanpathbot" bots
SetEnvIfNoCase User-Agent "pricebot" bots
SetEnvIfNoCase User-Agent "psbot" bots
SetEnvIfNoCase User-Agent "rogerbot" bots
SetEnvIfNoCase User-Agent "seoscanners" bots
SetEnvIfNoCase User-Agent "smtbot" bots
SetEnvIfNoCase User-Agent "spbot" bots
SetEnvIfNoCase User-Agent "spider" bots
SetEnvIfNoCase User-Agent "ssearch_bot" bots
SetEnvIfNoCase User-Agent "tb-webde" bots
SetEnvIfNoCase User-Agent "textlink_status_crawler" bots
SetEnvIfNoCase User-Agent "tracemyfile" bots
SetEnvIfNoCase User-Agent "trendictionbot" bots
SetEnvIfNoCase User-Agent "vebidoobot-Robot" bots
#### Retry 12h
Header always set Retry-After "43200" env=bots
RewriteEngine on
RewriteCond %{ENV:bots} 1
RewriteRule .* - [R=503,L]
################################################
################################################
#</mittwald 503 for bots>
<IfModule mod_headers.c>
Header set Strict-Transport-Security: "max-age=63072000; includeSubDomains; preload"
</IfModule>
### Begin: Compression ###
@@ -108,8 +222,8 @@ AddEncoding gzip .gzip
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/manifest+json "access plus 1 week"
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
ExpiresByType application/x-web-app-manifest+json "access plus 1 second"
ExpiresByType text/cache-manifest "access plus 1 second"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/bmp "access plus 1 month"
@@ -235,8 +349,12 @@ AddDefaultCharset utf-8
<IfModule mod_rewrite.c>
# Enable URL rewriting
RewriteEngine On
# Enable URL rewriting
RewriteEngine On
RewriteRule (wp-content|wp-admin|wp-login) https://www.ep-reisen.de/sinkhole.html [L,R=301,NC]
RewriteRule (rss|feeds|thread|threads|posts) https://www.ep-reisen.de/sinkhole.html [L,R=301,NC]
RewriteRule (forum|forums|member|members) https://www.ep-reisen.de/sinkhole.html [L,R=301,NC]
# Begin ep-events.de redirects
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
@@ -393,8 +511,8 @@ AddDefaultCharset utf-8
RewriteRule ^skireise-ins-val-di-sole-almazzago https://www.ep-events.de/team-building/ [L,R=301,NC]
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
RewriteRule ^firmenveranstaltungen/sports-active/bagger-bzw-panzerfahren https://www.ep-events.de/agentur/programm-ideen/ [L,R=301,NC]
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
RewriteRule ^Firmenveranstaltungen https://www.ep-events.de/ [L,R=301,NC]
#RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
#RewriteRule ^Firmenveranstaltungen https://www.ep-events.de/ [L,R=301,NC]
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
RewriteRule ^firmenveranstaltungen/sports-active/orientierungs-erlebniswanderung https://www.ep-events.de/agentur/programm-ideen/ [L,R=301,NC]
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de [NC]
@@ -2881,11 +2999,16 @@ AddDefaultCharset utf-8
RewriteCond %{HTTP_HOST} !^www\.ski\-boarderweek\.de
RewriteCond %{HTTP_HOST} !^skimax\.ep\-reisen\.de
RewriteCond %{HTTP_HOST} !^schnee\-event\.ep\-reisen\.de
RewriteCond %{HTTP_HOST} !^(www\.)?eventkonzept\.de
RewriteCond %{HTTP_HOST} !^www\.ep\-reisen\.de
RewriteRule (.*) https://www.ep-reisen.de/ [R=301,L]
# End redirect catchall subdomains
# Force SSL for all domains
# Redirect eventkonzept.de for the time being
RewriteCond %{HTTP_HOST} ^(www\.)?eventkonzept\.de
RewriteRule (.*) https://www.ep-events.de/ [R=302,L]
# Force SSL for all domains
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]
@@ -2895,12 +3018,20 @@ AddDefaultCharset utf-8
RewriteRule ^.*$ - [E=CWD:%2]
# Rules to set ApplicationContext based on hostname
#RewriteCond %{HTTP_HOST} ^dev\.example\.com$
#RewriteRule .? - [E=TYPO3_CONTEXT:Development]
#RewriteCond %{HTTP_HOST} ^staging\.example\.com$
#RewriteRule .? - [E=TYPO3_CONTEXT:Production/Staging]
#RewriteCond %{HTTP_HOST} ^www\.example\.com$
#RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^schnee\-event\.ep\-reisen\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production/Skimax]
RewriteCond %{HTTP_HOST} ^www\.ep\-reisen\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^www\.ep\-events\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^www\.ski\-wochenenden\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^www\.ski\-boarderweek\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^www\.unichamp\.de$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
RewriteCond %{HTTP_HOST} ^www\.snowzone\.net$
RewriteRule .? - [E=TYPO3_CONTEXT:Production]
# Rule for versioned static files, configured through:
# - $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename']
@@ -2927,6 +3058,165 @@ AddDefaultCharset utf-8
# NOTE: Add your additional local storages here
RewriteRule ^(?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]
### Begin: StaticFileCache (preparation) ####
# Document root configuration
RewriteRule .* - [E=SFC_ROOT:%{DOCUMENT_ROOT}]
# Cleanup URI
RewriteCond %{REQUEST_URI} ^.*$
RewriteRule .* - [E=SFC_URI:/%{REQUEST_URI}]
RewriteCond %{REQUEST_URI} ^/.*$
RewriteRule .* - [E=SFC_URI:%{REQUEST_URI}]
RewriteCond %{REQUEST_URI} ^/?$
RewriteRule .* - [E=SFC_URI:/]
# Cleanup HOST
RewriteCond %{HTTP_HOST} ^([^:]+)(:[0-9]+)?$
RewriteRule .* - [E=SFC_HOST:%1]
# Disable cache for EXT:solr indexing requests
RewriteCond %{HTTP:X-Tx-Solr-Iq} .+
RewriteRule .* - [E=SFC_HOST:invalid-host]
# Get scheme
RewriteRule .* - [E=SFC_PROTOCOL:http]
RewriteCond %{SERVER_PORT} ^443$ [OR]
RewriteCond %{HTTP:X-Forwarded-Proto} https
RewriteRule .* - [E=SFC_PROTOCOL:https]
# Get port
RewriteRule .* - [E=SFC_PORT:80]
RewriteCond %{ENV:SFC_PROTOCOL} ^https$ [NC]
RewriteRule .* - [E=SFC_PORT:443]
RewriteCond %{SERVER_PORT} ^[0-9]+$
RewriteRule .* - [E=SFC_PORT:%{SERVER_PORT}]
RewriteCond %{HTTP:X-Forwarded-Port} ^[0-9]+$
RewriteRule .* - [E=SFC_PORT:%{HTTP:X-Forwarded-Port}]
# Full path for redirect
RewriteRule .* - [E=SFC_FULLPATH:typo3temp/tx_staticfilecache/%{ENV:SFC_PROTOCOL}_%{ENV:SFC_HOST}_%{ENV:SFC_PORT}%{ENV:SFC_URI}/index]
# Extension (Order: br, gzip, default)
RewriteRule .* - [E=SFC_EXT:]
RewriteCond %{HTTP:Accept-Encoding} br [NC]
RewriteRule .* - [E=SFC_EXT:.br]
RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT} !-f
RewriteRule .* - [E=SFC_EXT:]
RewriteCond %{ENV:SFC_EXT} ^$
RewriteCond %{HTTP:Accept-Encoding} gzip [NC]
RewriteRule .* - [E=SFC_EXT:.gz]
RewriteCond %{ENV:SFC_EXT} ^\.gz$
RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT} !-f
RewriteRule .* - [E=SFC_EXT:]
# Write Extension to SFC_FULLPATH
RewriteRule .* - [E=SFC_FULLPATH:%{ENV:SFC_FULLPATH}%{ENV:SFC_EXT}]
### Begin: StaticFileCache (main) ####
# We only redirect URI's without query strings
RewriteCond %{QUERY_STRING} ^$
# It only makes sense to do the other checks if a static file actually exists.
RewriteCond %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH} -f
# NO frontend or backend user is logged in. Logged in users may see different
# information than anonymous users. But the anonymous version is cached. So
# don't show the anonymous version to logged in users.
RewriteCond %{HTTP_COOKIE} !staticfilecache [NC]
# We only redirect GET requests
RewriteCond %{REQUEST_METHOD} GET
# Rewrite the request to the static file.
RewriteRule .* %{ENV:SFC_ROOT}/%{ENV:SFC_FULLPATH} [L]
# Do not allow direct call the cache entries
RewriteCond %{ENV:SFC_URI} ^/typo3temp/tx_staticfilecache/.*
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule .* - [F,L]
# Handle application cache
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*\.sfc$ %{ENV:CWD}index.php?eID=sfc_manifest [QSA,L]
### Begin: StaticFileCache (options) ####
# Set proper content type and encoding for gzipped html.
<FilesMatch "\.gzip$">
SetEnv no-gzip 1
SetEnv no-brotli 1
<IfModule mod_headers.c>
Header set Content-Encoding gzip
</IfModule>
</FilesMatch>
<FilesMatch "\.gz$">
SetEnv no-gzip 1
SetEnv no-brotli 1
<IfModule mod_headers.c>
Header set Content-Encoding gzip
</IfModule>
</FilesMatch>
<FilesMatch "\.br$">
SetEnv no-gzip 1
SetEnv no-brotli 1
<IfModule mod_headers.c>
Header set Content-Encoding br
</IfModule>
</FilesMatch>
# if there are same problems with ForceType, please try the AddType alternative
# Set proper content type gzipped html
<FilesMatch "\.html\.gzip$">
ForceType text/html
# AddType "text/html" .gzip
</FilesMatch>
<FilesMatch "\.xml\.gzip$">
ForceType text/xml
# AddType "text/xml" .gzip
</FilesMatch>
<FilesMatch "\.rss\.gzip$">
ForceType text/xml
# AddType "text/xml" .gzip
</FilesMatch>
<FilesMatch "\.html\.gz$">
ForceType text/html
# AddType "text/html" .gz
</FilesMatch>
<FilesMatch "\.xml\.gz$">
ForceType text/xml
# AddType "text/xml" .gz
</FilesMatch>
<FilesMatch "\.rss\.gz$">
ForceType text/xml
# AddType "text/xml" .gz
</FilesMatch>
<FilesMatch "\.html\.br$">
ForceType text/html
# AddType "text/html" .br
</FilesMatch>
<FilesMatch "\.xml\.br$">
ForceType text/xml
# AddType "text/xml" .br
</FilesMatch>
<FilesMatch "\.rss\.br$">
ForceType text/xml
# AddType "text/xml" .br
</FilesMatch>
# Avoid .br files being delivered with Content-Language: br headers
<IfModule mod_mime.c>
RemoveLanguage .br
</IfModule>
### End: StaticFileCache ###
# If the file/symlink/directory does not exist => Redirect to index.php.
# For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'.
RewriteCond %{REQUEST_FILENAME} !-f
+1 -1
View File
@@ -65,7 +65,7 @@ host('185.237.67.190')
'options' => ['delete', 'links'],
'timeout' => 300,
])
->set('cachetool_args', '--web --web-path={{release_path}}/public/ --web-url=https://www.ep-reisen.de')
->set('cachetool_args', '--web=SymfonyHttpClient --web-path={{release_path}}/public/ --web-url=https://www.ep-reisen.de')
;
task('deploy', [
+5 -5
View File
@@ -17,7 +17,7 @@
"bootstrap-sass": "^3.4.1",
"bourbon": "^4.3.2",
"cookieconsent": "^3.1.0",
"dayjs": "^1.10.4",
"dayjs": "^1.10.6",
"flatpickr": "^4.6.9",
"font-awesome": "^4.7.0",
"jquery": "^1.12.4",
@@ -30,22 +30,22 @@
"sticky-js": "^1.3.0",
"tailwindcss": "^1.8.10",
"tinycolor2": "^1.4.1",
"vue": "^2.6.12",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vue-scrollto": "^2.18.2",
"vue-session": "^1.0.0",
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/plugin-proposal-object-rest-spread": "^7.13.8",
"@babel/plugin-proposal-object-rest-spread": "^7.14.4",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@symfony/webpack-encore": "^0.28.3",
"autoprefixer": "^8.6.5",
"node-sass": "^4.14.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.3.1",
"vue-loader": "^15.9.6",
"vue-template-compiler": "^2.6.12"
"vue-loader": "^15.9.7",
"vue-template-compiler": "^2.6.14"
},
"browserslist": [
"last 2 versions",
@@ -1,5 +1,5 @@
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/Mod/WebLayout/BackendLayouts.tsconfig">
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/Mod/Wizards/NewContentElement.tsconfig">
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/Wizards/NewContentElement.tsconfig">
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/RTE.tsconfig">
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/TCEFORM.tsconfig">
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:ep_events/Configuration/PageTS/Mod/SHARED.tsconfig">
@@ -96,9 +96,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -114,9 +111,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -93,9 +93,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -111,9 +108,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -95,9 +95,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -113,9 +110,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +109,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -127,9 +124,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -94,9 +94,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -112,9 +109,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -32,8 +32,9 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
class ContingentCommandController extends Command
class ContingentCommand extends Command
{
/**
@@ -49,9 +50,11 @@ class ContingentCommandController extends Command
/**
* @param ContingentImportService $contingentImportService
*/
public function __construct(ContingentImportService $contingentImportService)
public function __construct()
{
$this->contingentImportService = $contingentImportService;
/** @var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->contingentImportService = $objectManager->get(ContingentImportService::class);
parent::__construct();
}
@@ -63,9 +66,10 @@ class ContingentCommandController extends Command
*/
public function execute(InputInterface $input, OutputInterface $output)
{
$path = GeneralUtility::getFileAbsFileName('fileadmin/xmlexportzimmer');
$count = $this->contingentImportService->import($path);
$count = $this->contingentImportService->import();
$output->writeln(sprintf('%d rows imported.', $count ));
return 0;
}
}
@@ -85,6 +85,8 @@ class DateCommandController extends Command
$settings = $this->getSettings();
$resellerPageUid = $settings['resellerExportPageUid'];
$this->cacheService->clearPageCache($resellerPageUid);
return 0;
}
/**
@@ -103,6 +103,8 @@ class ExportCommandController extends Command
$output->writeln('Exporting CSV (Google) for ' . $reseller->getName());
$this->exportCsv($reseller, 'google', $destinationFolder);
}
return 0;
}
/**
@@ -0,0 +1,85 @@
<?php
namespace EP\EpProducts\Command;
/***************************************************************
*
* Copyright notice
*
* (c) 2021 Björn Fromme <[email protected]>, 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\Service\GroupsSwissService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
class GroupsSwissCommand extends Command
{
/**
* @var GroupsSwissService
*/
private $groupsSwissService;
protected static $defaultName = 'ep:groups-swiss';
public function __construct()
{
/** @var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->groupsSwissService = $objectManager->get(GroupsSwissService::class);
parent::__construct();
}
protected function configure()
{
$this->addArgument(
'hotel',
InputArgument::REQUIRED,
'E&P hotel uid'
);
$this->addArgument(
'house',
InputArgument::REQUIRED,
'Groups.swiss house number'
);
$this->addArgument(
'key',
InputArgument::REQUIRED,
'Groups.swiss API key'
);
}
public function execute(InputInterface $input, OutputInterface $output)
{
$hotelUid = $input->getArgument('hotel');
$houseNumber = $input->getArgument('house');
$apiKey = $input->getArgument('key');
$success = $this->groupsSwissService->publishContingents($hotelUid, $houseNumber, $apiKey);
return $success ? 0 : 1;
}
}
@@ -55,6 +55,8 @@ class SnowreportCommandController extends Command
$this->snowreportImportService->downloadReportsXml();
$count = $this->snowreportImportService->importSnowReportData();
$output->writeln(sprintf('%d rows imported.', $count ));
return 0;
}
}
@@ -93,6 +93,9 @@ class AjaxGroupsPriceController extends ActionController
'dateFrom' => $groupsPriceInquiry->getDateFrom(),
'dateTo' => $groupsPriceInquiry->getDateTo(),
'pax' => $groupsPriceInquiry->getPax(),
'paxGroup1' => $groupsPriceInquiry->getPaxGroup1(),
'paxGroup2' => $groupsPriceInquiry->getPaxGroup2(),
'paxGroup3' => $groupsPriceInquiry->getPaxGroup3(),
'options' => $selectedOptions,
'board' => $groupsPriceInquiry->getBoard(),
'summary' => $groupsPriceInquiry->getSummary(),
@@ -66,8 +66,10 @@ class ConceptController extends ActionController
$conceptUid = $this->settings['conceptUid'];
$concept = $this->conceptRepository->findByIdentifier($conceptUid);
}
$teaserConceptUids = GeneralUtility::trimExplode(',', $this->settings['additionalConceptUids']);
array_unshift($teaserConceptUids, $concept->getUid());
$filterSettings = new FilterSettings();
$filterSettings->setConceptUids([$concept->getUid()]);
$filterSettings->setConceptUids($teaserConceptUids);
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
$limit = $this->settings['showMoreLink'] ? 4 : 0;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
@@ -66,10 +66,24 @@ class GroupsPriceInquiry
/**
* @var string
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/
protected $pax;
/**
* @var string
*/
protected $paxGroup1;
/**
* @var string
*/
protected $paxGroup2;
/**
* @var string
*/
protected $paxGroup3;
/**
* @var array
*/
@@ -197,6 +211,54 @@ class GroupsPriceInquiry
$this->pax = $pax;
}
/**
* @return string
*/
public function getPaxGroup1()
{
return $this->paxGroup1;
}
/**
* @param string $paxGroup1
*/
public function setPaxGroup1($paxGroup1)
{
$this->paxGroup1 = $paxGroup1;
}
/**
* @return string
*/
public function getPaxGroup2()
{
return $this->paxGroup2;
}
/**
* @param string $paxGroup2
*/
public function setPaxGroup2($paxGroup2)
{
$this->paxGroup2 = $paxGroup2;
}
/**
* @return string
*/
public function getPaxGroup3()
{
return $this->paxGroup3;
}
/**
* @param string $paxGroup3
*/
public function setPaxGroup3($paxGroup3)
{
$this->paxGroup3 = $paxGroup3;
}
/**
* @return array
*/
@@ -56,6 +56,26 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
*/
protected $priceAdditionalPerson;
/**
* @var float
*/
protected $priceAdditionalPersonGroup1;
/**
* @var float
*/
protected $priceAdditionalPersonGroup2;
/**
* @var float
*/
protected $priceAdditionalPersonGroup3;
/**
* @var bool
*/
protected $bonusCardIncluded = false;
/**
* @return \DateTime
*/
@@ -123,7 +143,7 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
/**
* @return float
*/
public function getPriceAdditionalPerson(): float
public function getPriceAdditionalPerson(): ?float
{
return $this->priceAdditionalPerson;
}
@@ -136,6 +156,70 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
$this->priceAdditionalPerson = $priceAdditionalPerson;
}
/**
* @return float
*/
public function getPriceAdditionalPersonGroup1(): ?float
{
return $this->priceAdditionalPersonGroup1;
}
/**
* @param float $priceAdditionalPersonGroup1
*/
public function setPriceAdditionalPersonGroup1(float $priceAdditionalPersonGroup1): void
{
$this->priceAdditionalPersonGroup1 = $priceAdditionalPersonGroup1;
}
/**
* @return float
*/
public function getPriceAdditionalPersonGroup2(): ?float
{
return $this->priceAdditionalPersonGroup2;
}
/**
* @param float $priceAdditionalPersonGroup2
*/
public function setPriceAdditionalPersonGroup2(float $priceAdditionalPersonGroup2): void
{
$this->priceAdditionalPersonGroup2 = $priceAdditionalPersonGroup2;
}
/**
* @return float
*/
public function getPriceAdditionalPersonGroup3(): ?float
{
return $this->priceAdditionalPersonGroup3;
}
/**
* @param float $priceAdditionalPersonGroup3
*/
public function setPriceAdditionalPersonGroup3(float $priceAdditionalPersonGroup3): void
{
$this->priceAdditionalPersonGroup3 = $priceAdditionalPersonGroup3;
}
/**
* @return bool
*/
public function isBonusCardIncluded(): bool
{
return $this->bonusCardIncluded;
}
/**
* @param bool $bonusCardIncluded
*/
public function setBonusCardIncluded(bool $bonusCardIncluded): void
{
$this->bonusCardIncluded = $bonusCardIncluded;
}
/**
* @return array
*/
@@ -148,6 +232,12 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
'personsIncluded' => $this->getPersonsIncluded(),
'price' => $this->getPrice(),
'priceAdditionalPerson' => $this->getPriceAdditionalPerson(),
'priceAdditionalPersonByGroup' => [
1 => $this->getPriceAdditionalPersonGroup1(),
2 => $this->getPriceAdditionalPersonGroup2(),
3 => $this->getPriceAdditionalPersonGroup3(),
],
'bonusCardIncluded' => $this->isBonusCardIncluded(),
];
}
}
@@ -27,7 +27,9 @@ namespace EP\EpProducts\Domain\Repository;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use CalendR\Event\EventInterface;
use CalendR\Event\Provider\ProviderInterface;
use Doctrine\DBAL\DBALException;
use EP\EpProducts\Domain\Model\Contingent;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Model\Product;
@@ -37,51 +39,43 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
{
/**
* @param \DateTime $dateFrom
* @param \DateTime $dateTo
* @param \DateTime $begin
* @param \DateTime|null $end
* @param array $options
* @return \CalendR\Event\EventInterface[]
* @throws \Doctrine\DBAL\DBALException
* @return EventInterface[]
* @throws DBALException
*/
public function getEvents(\DateTime $dateFrom, \DateTime $dateTo = null, array $options = [])
public function getEvents(\DateTime $begin, \DateTime $end = null, array $options = []): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setDefined(['hotel']);
$optionsResolver->setRequired(['hotel']);
$optionsResolver->setAllowedTypes('hotel', Hotel::class);
$resolvedOptions = $optionsResolver->resolve($options);
/** @var Hotel $hotel */
$hotel = $resolvedOptions['hotel'];
$hotelCode = $hotel->getCode();
$qb = $this->getDbConnection()->createQueryBuilder();
$qb
->select('status as status', 'date as date', 'min_price as minPrice', 'pax', 'available')
->from('tx_epproducts_domain_model_contingent')
->where('hotel_code = :hotelCode')
->andWhere('date >= :dateFrom')
->setParameter('hotelCode', $hotelCode)
->setParameter('dateFrom', $begin->format('Y-m-d'))
->orderBy('date')
;
$hotel = null;
if ($resolvedOptions['hotel']) {
/** @var Hotel $hotel */
$hotel = $resolvedOptions['hotel'];
$qb
->where('hotel = :hotel')
->setParameter('hotel', $hotel->getUid())
;
}
$qb
->andWhere('date >= :dateFrom')
->setParameter('dateFrom', $dateFrom->format('Y-m-d'))
;
if (null !== $dateTo) {
if (null !== $end) {
$qb
->andWhere('date <= :dateTo')
->setParameter('dateTo', $dateTo->format('Y-m-d'))
->setParameter('dateTo', $end->format('Y-m-d'))
;
}
$result = $qb->execute()->fetchAll();
$result = $qb->execute()->fetchAllAssociative();
$dates = [];
@@ -108,9 +102,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
foreach ($dates as $row)
{
if ($hotel) {
$row['hotel'] = $hotel;
}
$row['hotel'] = $hotel;
$events[] = Contingent::fromArray($row);
}
@@ -121,7 +113,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
* @param Hotel $hotel
* @param Product $product
* @return array
* @throws \Doctrine\DBAL\DBALException
* @throws DBALException
*/
public function getAvailableContingents(Hotel $hotel, Product $product)
{
@@ -145,7 +137,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
->orderBy('c.date')
;
return $qb->execute()->fetchAll();
return $qb->execute()->fetchAllAssociative();
}
/**
@@ -154,7 +146,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
* @param Hotel $hotel
* @param Product $product
* @return array
* @throws \Doctrine\DBAL\DBALException
* @throws DBALException
*/
public function getAvailableRooms(\DateTime $dateFrom, \DateTime $dateTo, Hotel $hotel, Product $product)
{
@@ -185,7 +177,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
->addOrderBy('c.min_price')
;
return $qb->execute()->fetchAll();
return $qb->execute()->fetchAllAssociative();
}
}
@@ -41,7 +41,13 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
use LoggerAwareTrait;
use DbConnectionTrait;
const ROOMCODE_STATUS = 'BelKal';
public const XML_PATH = 'fileadmin/xmlexportzimmer';
public const ROOMCODE_STATUS = 'BelKal';
/**
* @var string[]
*/
protected $ignoredRoomTypes = ['PDGS', 'TEAM'];
/**
* @var ConfigurationManagerInterface
@@ -78,15 +84,14 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
}
/**
* @param string $path
* @return int
* @throws \Doctrine\DBAL\DBALException
*/
public function import($path)
public function import()
{
$this->createTempTable();
$this->logger->info('starting product import');
$contingentCount = $this->parseXmlFilesInFolder($path);
$contingentCount = $this->parseXmlFilesInFolder();
$this->clearTable();
$this->copyTable();
$this->clearTempTable();
@@ -104,35 +109,50 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
}
/**
* @param string $path
* @return int
*/
public function parseXmlFilesInFolder($path)
public function parseXmlFilesInFolder()
{
$dateCount = 0;
$path = GeneralUtility::getFileAbsFileName(self::XML_PATH);
$folder = opendir($path);
while (($fileName = readdir($folder)) !== false) {
if (strpos($fileName, 'HotelZimmer') === 0) {
$filePath = $path . '/' . $fileName;
$dateCount += $this->parseContingentXmlFile($filePath);
$dateCount += $this->parseContingentXmlFile($path, $fileName);
}
}
return $dateCount;
}
/**
* @param string $file
* @param string $fileName
* @return int
*/
public function parseContingentXmlFile($file)
public function parseContingentXmlFile($path, $fileName)
{
$filePath = sprintf('%s/%s', $path , $fileName);
if (null === $xmlData = $this->loadXmlFile($filePath)) {
return 0;
}
return $this->parseContingent($xmlData);
}
public function loadXmlFile($file): ?\SimpleXMLElement
{
libxml_use_internal_errors (true);
if (!file_exists($file)) {
return null;
}
/** @var \SimpleXMLElement $xmlData */
if (!$xmlData = simplexml_load_string(file_get_contents($file))) {
libxml_clear_errors();
return 0;
return null;
}
return $this->parseContingent($xmlData);
return $xmlData;
}
/**
@@ -174,9 +194,9 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
protected function parseContingent(\SimpleXMLElement $xmlContingent)
{
$hotelCode = (string) $xmlContingent['code'];
$sql = 'SELECT h.uid uid
FROM tx_epproducts_domain_model_hotel h
LEFT JOIN tx_epproducts_domain_model_matchcode m ON (h.uid = m.entity AND m.entity_type = "hotel" AND m.deleted = 0)
$sql = 'SELECT h.uid uid
FROM tx_epproducts_domain_model_hotel h
LEFT JOIN tx_epproducts_domain_model_matchcode m ON (h.uid = m.entity AND m.entity_type = "hotel" AND m.deleted = 0)
WHERE h.deleted = 0 AND h.hidden = 0 AND h.code = :code OR m.code = :code
LIMIT 0,1';
$hotelData = $this->db->fetchAssoc($sql, ['code' => $hotelCode]);
@@ -187,14 +207,24 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
return 0;
}
$hotelData['busProId'] = (string) $xmlContingent['idbuspro'];
$hotelData['code'] = (string) $xmlContingent['code'];
if ($xmlContingent->idbuspro_kontingent_aus) {
$linkedProductId = (int)$xmlContingent->idbuspro_kontingent_aus;
$path = GeneralUtility::getFileAbsFileName(self::XML_PATH);
if (null === $xmlContingent = $this->loadXmlFile(sprintf('%s/HotelZimmer_%d.xml', $path, $linkedProductId))) {
return 0;
}
}
if (empty($xmlContingent->kapazitaeten)) {
$this->logger->warning('hotel has no contingents', ['code' => $hotelCode]);
return 0;
}
$hotelData['busProId'] = (string) $xmlContingent['idbuspro'];
$hotelData['code'] = $hotelCode;
$roomTypes = $this->parseRoomTypes($xmlContingent->unterbringungen);
return $this->parseRooms($xmlContingent->kapazitaeten, $hotelData, $roomTypes);
}
@@ -207,11 +237,15 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
$types = [];
foreach ($xmlTypes->unterbringung as $xmlType)
{
$types[(string) $xmlType['idbuspro']] = [
$idBusPro = (string) $xmlType['idbuspro'];
$code = (string) $xmlType['code'];
$types[] = [
'idBusPro' => $idBusPro,
'link' => isset($xmlType['idbuspro_kontingent_aus']) ? (string) $xmlType['idbuspro_kontingent_aus'] : null,
'pax' => (int) $xmlType['pax_max'],
'code' => (string) $xmlType['code'],
'code' => $code,
'label' => (string) $xmlType['zimmerbezeichnung'],
'ctrl' => (string) $xmlType['code'] === self::ROOMCODE_STATUS,
'ctrl' => $code === self::ROOMCODE_STATUS,
];
}
return $types;
@@ -227,63 +261,62 @@ class ContingentImportService implements SingletonInterface, LoggerAwareInterfac
protected function parseRooms(\SimpleXMLElement $xmlDates, array $hotelData, array $roomTypes)
{
$dateCount = 0;
foreach ($xmlDates->kapazitaet as $xmlDate)
{
foreach ($xmlDates->kapazitaet as $xmlDate) {
$date = \DateTime::createFromFormat('d.m.Y', (string) $xmlDate['termin']);
$status = Contingent::STATUS_OK;
foreach ($xmlDate->zimmerliste->children() as $xmlRoom)
{
$roomTypeBusProId = (string) $xmlRoom['idbuspro'];
if (!array_key_exists($roomTypeBusProId, $roomTypes)) {
foreach ($roomTypes as $roomType) {
$roomCode = $roomType['code'];
if (in_array($roomCode, $this->ignoredRoomTypes, true)) {
continue;
}
$roomType = $roomTypes[$roomTypeBusProId];
$roomCode = $roomType['code'];
$availablePax = 0;
$capacity = 0;
if ($roomType['ctrl'] === true) {
if ((string) $xmlRoom['status'] === 'A') {
$status = Contingent::STATUS_ONREQUEST;
}
if ((string) $xmlRoom['status'] === 'S') {
$status = Contingent::STATUS_BLOCKED;
}
} else {
$capacity = (int) $xmlRoom['kontingent'] * $roomType['pax'];
$availableRooms = (int) $xmlRoom['frei'] > 0 ? (int) $xmlRoom['frei'] : 0;
$availablePax = $availableRooms * $roomType['pax'];
}
if ($availablePax === 0 && $roomType['ctrl'] === false) {
//continue;
}
$minPrice = (int) $xmlRoom['abpreis'];
$minNights = (int) $xmlRoom['abpreis_naechte'];
$additionalNightMinPrice = (int) $xmlRoom['abpreis_verlaengerung'];
$additionalNightMinNights = (int) $xmlRoom['abpreis_verlaengerung_naechte'];
$roomLabel = $roomType['label'];
$this->db->insert(
'tx_epproducts_domain_model_contingent_temp',
[
'pid' => $this->pid,
'hotel' => $hotelData['uid'],
'hotel_code' => $hotelData['code'],
'hotel_bus_pro_id' => $hotelData['busProId'],
'room_code' => $roomCode,
'room_label' => $roomLabel,
'pax' => $capacity,
'available' => $availablePax,
'status' => $status,
'date' => $date->format('Y-m-d'),
'min_price' => $minPrice,
'min_nights' => $minNights,
'additional_night_min_price' => $additionalNightMinPrice,
'additional_night_min_nights' => $additionalNightMinNights,
]
);
$roomTypeBusProId = $roomType['link'] ?: $roomType['idBusPro'];
$xpath = sprintf('zimmerliste/zimmer[@idbuspro="%d"]', $roomTypeBusProId);
$xmlRoom = $xmlDate->xpath($xpath)[0];
$this->calculateContingent($xmlRoom, $roomType, $date, $hotelData);
}
$dateCount++;
}
return $dateCount;
}
protected function calculateContingent(\SimpleXMLElement $xmlRoom, array $roomType, \DateTime $date, array $hotelData): void
{
$availablePax = 0;
$capacity = 0;
$status = Contingent::STATUS_OK;
$isControlRoom = $roomType['ctrl'] === true;
if ($isControlRoom && (string) $xmlRoom['status'] === 'A') {
$status = Contingent::STATUS_ONREQUEST;
} elseif ($isControlRoom && (string) $xmlRoom['status'] === 'S') {
$status = Contingent::STATUS_BLOCKED;
} else {
$capacity = (int) $xmlRoom['kontingent'] * (int) $roomType['pax'];
$availableRooms = (int) $xmlRoom['frei'] > 0 ? (int) $xmlRoom['frei'] : 0;
$availablePax = $availableRooms * $roomType['pax'];
}
$minPrice = (int) $xmlRoom['abpreis'];
$minNights = (int) $xmlRoom['abpreis_naechte'];
$additionalNightMinPrice = (int) $xmlRoom['abpreis_verlaengerung'];
$additionalNightMinNights = (int) $xmlRoom['abpreis_verlaengerung_naechte'];
$roomLabel = $roomType['label'];
$this->db->insert(
'tx_epproducts_domain_model_contingent_temp',
[
'pid' => $this->pid,
'hotel' => $hotelData['uid'],
'hotel_code' => $hotelData['code'],
'hotel_bus_pro_id' => $hotelData['busProId'],
'room_code' => $roomType['code'],
'room_label' => $roomLabel,
'pax' => $capacity,
'available' => $availablePax,
'status' => $status,
'date' => $date->format('Y-m-d'),
'min_price' => $minPrice,
'min_nights' => $minNights,
'additional_night_min_price' => $additionalNightMinPrice,
'additional_night_min_nights' => $additionalNightMinNights,
]
);
}
}
@@ -240,14 +240,14 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
$dateCount = 0;
$sql = '
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,
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, p.season season,
cnt.name country_name, cnt.code country_code, cnt.keywords country_keywords, r.name region_name,
r.keywords region_keywords, r.feature region_feature, c.name city_name, c.keywords city_keywords,
cnt.name country_name, cnt.code country_code, cnt.keywords country_keywords, r.name region_name,
r.keywords region_keywords, r.feature region_feature, c.name city_name, c.keywords city_keywords,
p.concept concept, con.name concept_name, con.code concept_code, con.sorting concept_sorting
FROM tx_epproducts_domain_model_product p
FROM tx_epproducts_domain_model_product p
INNER JOIN tx_epproducts_domain_model_country cnt ON p.country = cnt.uid
INNER JOIN tx_epproducts_domain_model_region r ON p.region = r.uid
INNER JOIN tx_epproducts_domain_model_city c ON p.city = c.uid
@@ -727,7 +727,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
foreach ($selectionGroup as $selection)
{
$attributes = $selection->attributes();
$id = (int) $attributes['id'];
$id = (int) $attributes['idbuspro'];
$data[$id] = (string) $attributes['bezeichnung'];
}
@@ -767,7 +767,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
protected function getHotelMappings()
{
$sql = '
SELECT u.code code, u.uid uid, h.name name, h.type type,
SELECT u.code code, u.uid uid, h.name name, h.type type,
h.earlybird earlybird, h.new new, h.low_contingent low_contingent,
h.keywords keywords, h.header_title header_title,
h.category category, h.teaser teaser, h.detail_page detail_page
@@ -779,7 +779,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
FROM tx_epproducts_domain_model_matchcode matchcode
WHERE matchcode.deleted = 0 AND matchcode.hidden = 0
ORDER BY code
) u
) u
LEFT JOIN tx_epproducts_domain_model_hotel h ON u.uid = h.uid
';
$rows = $this->db->fetchAllAssociative($sql);
@@ -810,7 +810,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
protected function getTopFactForRecord($table, $uid)
{
$sql = '
SELECT f.name
SELECT f.name
FROM ' . $table . ' mm
INNER JOIN tx_epproducts_domain_model_fact f ON mm.uid_foreign = f.uid
WHERE mm.uid_local = :uid
@@ -26,7 +26,7 @@ namespace EP\EpProducts\Service;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception as DBALException;
use EP\EpProducts\Domain\Model\Contingent;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\ContingentRepository;
@@ -37,9 +37,6 @@ use Psr\Log\LoggerAwareTrait;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Configuration\Exception;
class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
{
@@ -55,11 +52,6 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
*/
protected $contingentRepository;
/**
* @var array
*/
protected $apiKeys;
/**
* @var HttpClient
*/
@@ -68,65 +60,48 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
/**
* @param HotelRepository $hotelRepository
* @param ContingentRepository $contingentRepository
* @param ConfigurationManagerInterface $configurationManager
*/
public function __construct(
HotelRepository $hotelRepository,
ContingentRepository $contingentRepository,
ConfigurationManagerInterface $configurationManager
) {
public function __construct(HotelRepository $hotelRepository, ContingentRepository $contingentRepository)
{
$this->hotelRepository = $hotelRepository;
$this->contingentRepository = $contingentRepository;
$settings = GeneralUtility::removeDotsFromTS(
$configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
)
);
$this->apiKeys = $settings['plugin']['tx_epproducts']['settings']['groupsSwissApiKeys'];
$this->httpClient = HttpClient::create();
}
/**
* @return void
* @throws Exception
*/
public function publishContingents(): void
public function publishContingents(int $hotelUid, int $houseNumber, string $apiKey): bool
{
$range = Period::after(new \DateTime('now'), '1 year');
foreach ($this->apiKeys as $hotelUid => $config) {
/** @var Hotel $hotel */
$hotel = $this->hotelRepository->findByUid($hotelUid);
$hotel = $this->hotelRepository->findByIdentifier($hotelUid);
if (null === $hotel) {
$this->logger->error(sprintf('Hotel with uid %d not found', $hotelUid));
continue;
}
if (null === $hotel) {
$this->logger->error(sprintf('Hotel with uid %d not found', $hotelUid));
return false;
}
$this->logger->info(sprintf(
'Publishing availabilities for hotel %s',
preg_replace( '/[\r\n]/', '', $hotel->getName())
));
try {
$events = $this->contingentRepository->getEvents(
\DateTime::createFromImmutable($range->getStartDate()),
\DateTime::createFromImmutable($range->getEndDate()),
['hotel' => $hotel]
);
}
catch (DBALException $e) {
$events = [];
}
$this->resetChart($range, $config['key'], $config['number']);
$this->generateChart(
$events,
$config['key'],
$config['number']
try {
$events = $this->contingentRepository->getEvents(
\DateTime::createFromImmutable($range->getStartDate()),
\DateTime::createFromImmutable($range->getEndDate()),
['hotel' => $hotel]
);
}
catch (DBALException $e) {
$this->logger->error(sprintf('No dates found for hotel with uid %d', $hotelUid));
return false;
}
$this->resetChart($range, $apiKey, $houseNumber);
if (count($events) > 0) {
$this->generateChart(
$events,
$apiKey,
$houseNumber
);
}
return true;
}
/**
@@ -151,7 +126,6 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
*/
protected function generateChart(array $events, $apiKey, $houseNumber): void
{
$itemCount = 0;
$item = null;
$status = null;
@@ -168,11 +142,8 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
$statusCode = Contingent::STATUS_ONREQUEST === $status ? 1 : 2;
$this->addItem($item, $statusCode, $apiKey, $houseNumber);
$item = null;
$itemCount++;
}
}
$this->logger->info(sprintf('Published %d dates.', $itemCount));
}
/**
@@ -206,8 +177,16 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
*/
protected function addItem(array $item, $status, $apiKey, $houseNumber): void
{
$this->logger->info(sprintf(
'Adding status %d in %s-%s for %s',
$status,
$item['from']->format('Y-m-d'),
$item['to']->format('Y-m-d'),
$houseNumber
));
$url = sprintf(
'http://www.groups.swiss/api/1.0/json.php?apiKey=%s&method=setAvailabilityChart&house=%d&from=%s&till=%s&state=%d',
'https://www.groups.swiss/api/1.0/json.php?apiKey=%s&method=setAvailabilityChart&house=%d&from=%s&till=%s&state=%d',
$apiKey,
$houseNumber,
$item['from']->format('Y-m-d'),
@@ -216,7 +195,8 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
);
try {
$this->httpClient->request('GET', $url);
$response = $this->httpClient->request('GET', $url);
$this->logger->info($response->getContent());
} catch (TransportExceptionInterface $e) {
$this->logger->error(sprintf('A problem occured: %s', $e->getMessage()));
}
@@ -15,6 +15,10 @@ class ImportTimestampService
$line = trim(file($lastImportTimestampPath)[0]);
if (empty($line)) {
return null;
}
return \DateTime::createFromFormat('d.m.Y H:i:s', $line);
}
@@ -30,10 +34,17 @@ class ImportTimestampService
$updatedTimestampPath = GeneralUtility::getFileAbsFileName($uploadTimestampFile);
if (!$updatedTimestampPath || !file_exists($updatedTimestampPath)) {
return false;
$this->writeImportTimestamp($importTimestampFile);
return true;
}
$line = trim(file($updatedTimestampPath)[0]);
if (empty($line)) {
return true;
}
$lastUploadAt = \DateTime::createFromFormat('d.m.Y H:i:s', $line);
$lastImportAt = $this->readImportTimestamp($importTimestampFile);
@@ -122,7 +122,7 @@ class SnowreportImportService implements SingletonInterface
$xmlContent = file_get_contents($file);
$xmlData = simplexml_load_string($xmlContent);
$snowReportStoragePid = SettingsUtility::get('snowReportStoragePid');
$snowReportStoragePid = $this->getStoragePid();
foreach ($xmlData->schneemeldung as $reportXml)
{
@@ -202,7 +202,7 @@ class SnowreportImportService implements SingletonInterface
*/
public function importWebcamData(\SimpleXMLElement $xml, Snowreport $snowreport)
{
$snowReportStoragePid = SettingsUtility::get('snowReportStoragePid');
$snowReportStoragePid = $this->getStoragePid();
foreach ($xml->children() as $item)
{
$include = (string) $item->aktuell === 'true';
@@ -243,7 +243,7 @@ class SnowreportImportService implements SingletonInterface
->select('vendor_uid')
->from($table)
->execute()
->fetchAll()
->fetchAllAssociative()
;
$currentUids = array_map(function ($row) {
@@ -282,4 +282,10 @@ class SnowreportImportService implements SingletonInterface
@rename($backupPath, $path);
}
public function getStoragePid()
{
$settingsUtility = new SettingsUtility();
return $settingsUtility->get('snowReportStoragePid');
}
}
@@ -1,51 +0,0 @@
<?php
namespace EP\EpProducts\Task;
/***************************************************************
*
* Copyright notice
*
* (c) 2020 Björn Fromme <[email protected]>, 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\Service\GroupsSwissService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\Exception;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Scheduler\Task\AbstractTask;
class GroupsSwissTask extends AbstractTask
{
public function execute()
{
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** @var GroupsSwissService $groupsSwissService */
$groupsSwissService = $objectManager->get(GroupsSwissService::class);
try {
$groupsSwissService->publishContingents();
} catch (Exception $e) {
}
return true;
}
}
@@ -41,8 +41,7 @@ class ImportContingentsTask extends AbstractTask
/** @var \EP\EpProducts\Service\ContingentImportService $importService */
$importService = $objectManager->get(ContingentImportService::class);
if ($importService->isImportRequired()) {
$path = GeneralUtility::getFileAbsFileName('fileadmin/xmlexportzimmer');
$importService->import($path);
$importService->import();
}
return true;
@@ -34,23 +34,32 @@ use TYPO3\CMS\Extbase\Object\ObjectManager;
class SettingsUtility
{
/**
* @var array
*/
protected $settings = [];
public function __construct()
{
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$configurationManager = $objectManager->get(ConfigurationManager::class);
$settings = GeneralUtility::removeDotsFromTS(
$configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT)
);
$this->settings = $settings['plugin']['tx_epproducts']['settings'];
}
/**
* @param string $key
* @return string|array
*/
static public function get($key = null)
public function get($key = null)
{
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$configurationManager = $objectManager->get(ConfigurationManager::class);
$settings = GeneralUtility::removeDotsFromTS($configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT));
if ($key !== null && array_key_exists($key, $settings['plugin']['tx_epproducts']['settings'])) {
return $settings['plugin']['tx_epproducts']['settings'][$key];
if ($key !== null && array_key_exists($key, $this->settings)) {
return $this->settings[$key];
}
return $settings['plugin']['tx_epproducts']['settings'];
return $this->settings;
}
}
}
@@ -5,4 +5,11 @@ return [
'class' => \EP\EpProducts\Command\ExportCommandController::class,
'scheduleable' => false,
],
'ep:groups-swiss' => [
'class' => \EP\EpProducts\Command\GroupsSwissCommand::class,
],
'ep:contingent-import' => [
'class' => \EP\EpProducts\Command\ContingentCommand::class,
'scheduleable' => false,
],
];
@@ -25,11 +25,25 @@
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</TCEforms>
</settings.conceptUid>
<settings.additionalConceptUids>
<TCEforms>
<label>Weitere Konzepte für Teaser:</label>
<config>
<type>select</type>
<renderType>selectSingleBox</renderType>
<foreign_table>tx_epproducts_domain_model_concept</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_concept.hidden = 0 AND tx_epproducts_domain_model_concept.deleted = 0 ORDER BY tx_epproducts_domain_model_concept.name</foreign_table_where>
<size>5</size>
<minitems>0</minitems>
<maxitems>10</maxitems>
</config>
</TCEforms>
</settings.additionalConceptUids>
<settings.showMoreLink>
<TCEforms>
<exclude>0</exclude>
@@ -92,9 +92,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -110,9 +107,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -95,9 +95,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -113,9 +110,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -99,9 +99,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -117,9 +114,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -101,9 +101,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -119,9 +116,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -97,9 +97,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -115,9 +112,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -92,9 +92,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -110,9 +107,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -94,9 +94,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -112,9 +109,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -25,16 +25,20 @@ return [
],
'interface' => [
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, date_from, date_to,
persons_included, price, price_additional_person',
persons_included, bonus_card_included, price, price_additional_person,
price_additional_person_group_1, price_additional_person_group_2, price_additional_person_group_3',
],
'types' => [
'1' => [
'showitem' => '--palette--;;dates, --palette--;;prices, persons_included',
'showitem' => '--palette--;;dates, --palette--;;included, --palette--;;prices, --palette--;;prices_groups',
],
],
'palettes' => [
'dates' => ['showitem' => 'date_from, date_to'],
'included' => ['showitem' => 'bonus_card_included, persons_included'],
'prices' => ['showitem' => 'price, price_additional_person'],
'prices_groups' => ['showitem' => 'price_additional_person_group_1, price_additional_person_group_2,
--linebreak--, price_additional_person_group_3'],
],
'columns' => [
@@ -50,6 +54,7 @@ return [
['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1],
['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0]
],
'default' => -1,
],
],
'l10n_parent' => [
@@ -97,9 +102,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -115,9 +117,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -166,11 +165,49 @@ return [
'price_additional_person' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'ohne Jokercard, alle Altersgruppen',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2,required',
'eval' => 'double2',
]
],
'price_additional_person_group_1' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, 4-6 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'price_additional_person_group_2' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, 7-14 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'price_additional_person_group_3' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, ab 15 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'bonus_card_included' => [
'exclude' => 0,
'label' => 'Jokercard inklusive',
'config' => [
'type' => 'check',
],
],
],
];
@@ -96,9 +96,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -114,9 +111,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +109,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -127,9 +124,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -90,9 +90,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -108,9 +105,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -23,22 +23,21 @@ return [
'iconfile' => 'EXT:ep_theme/Resources/Public/images/icon_ce.svg',
],
'interface' => [
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, searchable, bookable,
season, detail_page, name, name_internal, subline, keywords, headline, code, feature, bus_pro_id, teaser,
teaser_long, 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,
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, searchable, bookable,
season, detail_page, name, name_internal, subline, keywords, headline, code, feature, bus_pro_id, teaser,
teaser_long, 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, hide_booking_button,non_bookable_dates',
],
'types' => [
'1' => [
'showitem' => '--palette--;;top, name_internal, --palette--;;name, --palette--;;header,
--palette--;;destinations, keywords, feature, --palette--;Alternative;alternative,
--div--;Texte, teaser, teaser_long, concept_description, board_description, additional_services,
'showitem' => '--palette--;;top, name_internal, --palette--;;name, --palette--;;header,
--palette--;;destinations, --palette--;;misc, --palette--;Alternative;alternative,
--div--;Texte, teaser, teaser_long, concept_description, board_description, additional_services,
programme_description, ski_pass_description,
--div--;Verknüpfungen, journey, --palette--;;concept, hotels, facts, faqs, officetip,
additional_regions, badge,
--div--;Verknüpfungen, --palette--;;records, hotels, facts, faqs, additional_regions,
--div--;Bilder/Dateien, teaser_images, header_images, images, reseller_images, banner, video,
--div--;Bewertung, --palette--;;rating, testimonials,
--div--;Buchbarkeit, non_bookable_dates',
@@ -46,13 +45,14 @@ return [
],
'palettes' => [
'destinations' => ['showitem' => 'country,region,city'],
'top' => ['showitem' => 'searchable, bookable, hide_booking_button, daytrip, important, skipass_included,
'top' => ['showitem' => 'searchable, bookable, hide_booking_button, daytrip, important, skipass_included,
season, 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'],
'rating' => ['showitem' => 'rating_average,rating_votes_count'],
'concept' => ['showitem' => 'concept,calendar_hotel'],
'header' => ['showitem' => 'headline, subline, --linebreak--, header_title, header_subtitle'],
'rating' => ['showitem' => 'rating_average, rating_votes_count'],
'records' => ['showitem' => 'journey, concept, calendar_hotel, officetip, badge'],
'misc' => ['showitem' => 'feature,keywords'],
],
'columns' => [
@@ -115,9 +115,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -133,9 +130,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -245,11 +239,12 @@ return [
],
'keywords' => [
'exclude' => 0,
'label' => 'Keywords für Suchschlitz',
'label' => 'Keywords',
'description' => 'für Suchschlitz (veraltet)',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 3,
'rows' => 2,
'eval' => 'trim',
],
],
@@ -273,10 +268,12 @@ return [
],
'feature' => [
'exclude' => 0,
'label' => 'Leistung für Teaser',
'label' => 'Leistungen',
'description' => 'für Teaser, max 2',
'config' => [
'type' => 'input',
'size' => 30,
'type' => 'text',
'cols' => 30,
'rows' => 2,
'eval' => 'trim'
],
],
@@ -106,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -124,9 +121,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -89,9 +89,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -107,9 +104,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -91,9 +91,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -109,9 +106,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -90,9 +90,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -108,9 +105,6 @@ return [
'eval' => 'date',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -93,9 +93,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -111,9 +108,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -97,9 +97,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -115,9 +112,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -90,9 +90,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -108,9 +105,6 @@ return [
'eval' => 'date',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -67,36 +67,37 @@ plugin.tx_epproducts {
groupsSwissApiKeys {
# Spinabad
7 {
hotelCode = DGS
number = 9849
key = a8b504ab2784446cbf73b8b1f6777f43
}
# Waldschlössli
6 {
hotelCode = DPW
number = 9658
key = f757b34dc3ac4a158257cf12c9ce69e1
}
# Jenatsch
37 {
hotelCode = LPJNEU
number = 9703
key = 52c314a88a434b359717115db992d20d
}
# Schweizerhaus
5 {
hotelCode = DKS
number = 9850
key = 4a955b23158d40c583b2892a18aa1043
}
# Astoria
74 {
number = 632795
key = 7002bbee9fab4985b1caccabb6f6cab5
}
# Klein Tirol
65 {
hotelCode = MVK
number = 668904
key = e9d86dbc465848d7a0b68df14e3e482f
}
# Weissfluh
131 {
hotelCode = DWW21/22
number = 961523
key = 6a22585d23184f2090bd7110aa9b60f3
}
+31 -27
View File
@@ -280,37 +280,41 @@ CREATE TABLE tx_epproducts_domain_model_hotel
CREATE TABLE tx_epproducts_domain_model_groupspriceconfig
(
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
date_from int(11) unsigned DEFAULT '0' NOT NULL,
date_to int(11) unsigned DEFAULT '0' NOT NULL,
persons_included int(11) unsigned DEFAULT '0' NOT NULL,
price float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person float(8) unsigned DEFAULT '0' NOT NULL,
hotel int(11) unsigned DEFAULT '0',
date_from int(11) unsigned DEFAULT '0' NOT NULL,
date_to int(11) unsigned DEFAULT '0' NOT NULL,
persons_included int(11) unsigned DEFAULT '0' NOT NULL,
price float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_1 float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_2 float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_3 float(8) unsigned DEFAULT '0' NOT NULL,
bonus_card_included tinyint(4) unsigned DEFAULT '0' NOT NULL,
hotel int(11) unsigned DEFAULT '0',
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL,
cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
starttime int(11) unsigned DEFAULT '0' NOT NULL,
endtime int(11) unsigned DEFAULT '0' NOT NULL,
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL,
cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
starttime int(11) unsigned DEFAULT '0' NOT NULL,
endtime int(11) unsigned DEFAULT '0' NOT NULL,
t3ver_oid int(11) DEFAULT '0' NOT NULL,
t3ver_id int(11) DEFAULT '0' NOT NULL,
t3ver_wsid int(11) DEFAULT '0' NOT NULL,
t3ver_label varchar(255) DEFAULT '' NOT NULL,
t3ver_state tinyint(4) DEFAULT '0' NOT NULL,
t3ver_stage int(11) DEFAULT '0' NOT NULL,
t3ver_count int(11) DEFAULT '0' NOT NULL,
t3ver_tstamp int(11) DEFAULT '0' NOT NULL,
t3ver_move_id int(11) DEFAULT '0' NOT NULL,
t3ver_oid int(11) DEFAULT '0' NOT NULL,
t3ver_id int(11) DEFAULT '0' NOT NULL,
t3ver_wsid int(11) DEFAULT '0' NOT NULL,
t3ver_label varchar(255) DEFAULT '' NOT NULL,
t3ver_state tinyint(4) DEFAULT '0' NOT NULL,
t3ver_stage int(11) DEFAULT '0' NOT NULL,
t3ver_count int(11) DEFAULT '0' NOT NULL,
t3ver_tstamp int(11) DEFAULT '0' NOT NULL,
t3ver_move_id int(11) DEFAULT '0' NOT NULL,
sys_language_uid int(11) DEFAULT '0' NOT NULL,
l10n_parent int(11) DEFAULT '0' NOT NULL,
l10n_diffsource mediumblob,
sys_language_uid int(11) DEFAULT '0' NOT NULL,
l10n_parent int(11) DEFAULT '0' NOT NULL,
l10n_diffsource mediumblob,
PRIMARY KEY (uid),
KEY parent (pid),
@@ -93,9 +93,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -111,9 +108,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -95,9 +95,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -113,9 +110,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -97,9 +97,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -115,9 +112,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -6,7 +6,23 @@
</div>
<div class="form-group" v-show="!submitted">
<label>Personenzahl</label>
<input type="number" class="form-control" v-model="selectedPax" @change="onPaxUpdated()"/>
<input type="number" class="form-control" v-model.number="selectedPax" @change="onPaxUpdated()"
v-show="nightsCount > 0 && !includesBonusCard"/>
<div class="flex space-x-4" v-show="nightsCount > 0 && includesBonusCard">
<div class="w-1/3 flex flex-col space-y-1">
<span>ab 15 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup3" @change="onPaxUpdated()"/>
</div>
<div class="w-1/3 flex flex-col space-y-1">
<span>7-14 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup2" @change="onPaxUpdated()"/>
</div>
<div class="w-1/3 flex flex-col space-y-1">
<span>4-6 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup1" @change="onPaxUpdated()"/>
</div>
</div>
<p class="text-sm italic" v-show="nightsCount === 0">Bitte wählen Sie einen Zeitraum</p>
</div>
<div class="form-group" v-show="options.length > 0 && !submitted">
<label>Optionale Zusatzleistungen</label>
@@ -20,11 +36,13 @@
</div>
<div class="form-group" v-show="boards.length > 0 && !submitted">
<label>Optionale Verpflegungsleistungen</label>
<select class="form-control" v-model="selectedBoard" v-if="selectedPax >= 30">
<select class="form-control" v-model="selectedBoard" v-if="totalPax >= 30">
<option :value="null">Keine Auswahl</option>
<option v-for="board of boards" :value="board" :key="board.uid">{{ board.title }} ({{ formatCurrency(board.price) }} pro Person und Nacht)</option>
<option v-for="board of boards" :value="board" :key="board.uid">{{ board.title }}
({{ formatCurrency(board.price) }} pro Person und Nacht)
</option>
</select>
<div v-show="selectedPax < 30">
<div v-show="totalPax < 30">
<span class="label label-warning">Erst ab 30 Personen buchbar.</span>
</div>
</div>
@@ -33,38 +51,47 @@
<div class="panel-body">
<table class="table table-striped" v-show="nightsCount > 0">
<tbody>
<tr>
<th>Zeitraum</th>
<td>{{ rangeFormatted }}, {{ nightsCount }} Nächte</td>
</tr>
<tr>
<th>Basispreis</th>
<td>{{ formatCurrency(pricePax.base) }}</td>
</tr>
<tr v-if="pricePax.additional > 0">
<th>Aufpreis Personenzahl</th>
<td>{{ formatCurrency(pricePax.additional) }}</td>
</tr>
<tr v-if="priceShortTerm > 0">
<th>Aufpreis Kurzzeit</th>
<td>{{ formatCurrency(priceShortTerm) }}</td>
</tr>
<tr v-if="selectedOptions.length > 0">
<th>Zusatzleistungen</th>
<td>{{ formatCurrency(priceOptions) }}</td>
</tr>
<tr v-if="priceBoard > 0">
<th>Verpflegung</th>
<td>{{ formatCurrency(priceBoard) }}</td>
</tr>
<tr v-if="priceRunningCosts > 0">
<th>Strom- und Abfallgebühren</th>
<td>{{ formatCurrency(priceRunningCosts) }}</td>
</tr>
<tr>
<th>Gesamtpreis</th>
<td><strong>{{ formatCurrency(priceTotal) }}</strong><br><small>zzgl. vor Ort zu entrichtender Ortstaxe</small></td>
</tr>
<tr>
<th>Zeitraum</th>
<td>{{ rangeFormatted }}, {{ nightsCount }} Nächte</td>
</tr>
<tr>
<th>Basispreis</th>
<td>{{ formatCurrency(pricePax.base) }}</td>
</tr>
<tr v-if="pricePax.additional > 0">
<th>Aufpreis Personenzahl</th>
<td>{{ formatCurrency(pricePax.additional) }}</td>
</tr>
<tr v-if="priceShortTerm > 0">
<th>Aufpreis Kurzzeit</th>
<td>{{ formatCurrency(priceShortTerm) }}</td>
</tr>
<tr v-if="includesBonusCard">
<th>Inklusivleistungen</th>
<td>
<a href="https://www.saalbach.com/de/sommer/joker-card" target="_blank" class="uppercase">
Joker Card
</a>
</td>
</tr>
<tr v-if="selectedOptions.length > 0">
<th>Zusatzleistungen</th>
<td>{{ formatCurrency(priceOptions) }}</td>
</tr>
<tr v-if="priceBoard > 0">
<th>Verpflegung</th>
<td>{{ formatCurrency(priceBoard) }}</td>
</tr>
<tr v-if="priceRunningCosts > 0">
<th>Strom- und Abfallgebühren</th>
<td>{{ formatCurrency(priceRunningCosts) }}</td>
</tr>
<tr>
<th>Gesamtpreis</th>
<td><strong>{{ formatCurrency(priceTotal) }}</strong><br><small>zzgl. vor Ort zu entrichtender
Ortstaxe</small></td>
</tr>
</tbody>
</table>
<div v-show="nightsCount === 0">
@@ -114,260 +141,316 @@
</template>
<script>
import $ from 'jquery';
import dayjs from 'dayjs'
import 'dayjs/locale/de'
import 'flatpickr';
import { German } from 'flatpickr/dist/l10n/de';
import Vue from 'vue';
import axios from 'axios';
import $ from 'jquery';
import dayjs from 'dayjs'
import 'dayjs/locale/de'
import 'flatpickr';
import {German} from 'flatpickr/dist/l10n/de';
import Vue from 'vue';
import axios from 'axios';
export default {
props: {
loaderUri: {
type: String,
required: true
},
endpoint: {
type: String,
required: true
},
configs: {
type: Array,
required: true
},
boards: {
type: Array,
required: true
},
options: {
type: Array,
required: true
export default {
props: {
loaderUri: {
type: String,
required: true
},
endpoint: {
type: String,
required: true
},
configs: {
type: Array,
required: true
},
boards: {
type: Array,
required: true
},
options: {
type: Array,
required: true
},
runningCostsFactor: {
type: Number,
default: 1.7,
},
shortTermFactors: {
type: Object,
default() {
return {
2: 0.2,
3: 0.1,
}
}
},
data () {
return {
processing: false,
submitted: false,
picker: null,
formErrors: {},
selectedFrom: null,
selectedTo: null,
selectedRange: [],
selectedPax: 30,
selectedOptions: [],
selectedBoard: null,
name: null,
email: null,
phone: null,
remarks: null
},
data() {
return {
processing: false,
submitted: false,
picker: null,
formErrors: {},
selectedFrom: null,
selectedTo: null,
selectedRange: [],
selectedPax: 30,
selectedPaxGroup1: 0,
selectedPaxGroup2: 0,
selectedPaxGroup3: 30,
selectedOptions: [],
selectedBoard: null,
name: null,
email: null,
phone: null,
remarks: null
}
},
methods: {
formatCurrency(value) {
if (0 === value) {
return '-';
}
return parseFloat(value).toFixed(2).replace(/\./, ',') + ' €';
},
methods: {
formatCurrency (value) {
if (0 === value) {
return '-';
}
return parseFloat(value).toFixed(2).replace(/\./, ',') + ' ';
},
formatOptionPriceType (option) {
if (2 === option.type) {
return ' ('+this.formatCurrency(option.price)+' pro Person)';
}
if (3 === option.type) {
return ' ('+this.formatCurrency(option.price)+' pro Nacht)';
}
if (4 === option.type) {
return ' ('+this.formatCurrency(option.price)+' pro Person und Nacht)';
}
formatOptionPriceType(option) {
if (2 === option.type) {
return ' (' + this.formatCurrency(option.price) + ' pro Person)';
}
if (3 === option.type) {
return ' (' + this.formatCurrency(option.price) + ' pro Nacht)';
}
if (4 === option.type) {
return ' (' + this.formatCurrency(option.price) + ' pro Person und Nacht)';
}
return ' ('+this.formatCurrency(option.price)+')';
},
initSelectableRanges () {
let enabledDates = [];
for (let config of this.configs) {
const dateFrom = dayjs(config.dateFrom);
const dateTo = dayjs(config.dateTo);
enabledDates.push({
from: dateFrom.format('DD.MM.YYYY'),
to: dateTo.format('DD.MM.YYYY')
});
}
this.picker.set('enable', enabledDates);
if (enabledDates.length > 0) {
const firstDate = enabledDates[0].from;
this.picker.set('minDate', firstDate);
}
},
updateSelectedRange () {
let range = [];
let currentDate = this.selectedFrom;
while (currentDate < this.selectedTo) {
range.push(currentDate);
currentDate = currentDate.add(1, 'day');
}
this.selectedRange = range;
},
onPaxUpdated () {
if (this.selectedPax < 30) {
this.selectedBoard = null;
}
},
submitForm () {
let options = [];
for (let option of this.selectedOptions) {
options.push(option.uid);
}
let formData = {
'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name,
'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email,
'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone,
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.selectedPax,
'tx_epproducts_ajax[groupsPriceInquiry][board]': this.selectedBoard ? this.selectedBoard.uid : null,
'tx_epproducts_ajax[groupsPriceInquiry][options]': options,
'tx_epproducts_ajax[groupsPriceInquiry][summary]': this.priceSummary
};
this.processing = true;
this.submitted = false;
axios({
url: this.endpoint,
method: 'POST',
data: $.param(formData)
}).then(response => {
if ('validation' === response.data.status) {
this.formErrors = response.data.errors;
} else {
this.submitted = true;
}
}).catch(error => {
}).finally(() => {
this.processing = false;
return ' (' + this.formatCurrency(option.price) + ')';
},
initSelectableRanges() {
let enabledDates = [];
for (let config of this.configs) {
const dateFrom = dayjs(config.dateFrom);
const dateTo = dayjs(config.dateTo);
enabledDates.push({
from: dateFrom.format('DD.MM.YYYY'),
to: dateTo.format('DD.MM.YYYY')
});
}
this.picker.set('enable', enabledDates);
if (enabledDates.length > 0) {
const firstDate = enabledDates[0].from;
this.picker.set('minDate', firstDate);
}
},
computed: {
nightsCount () {
return this.selectedRange.length;
},
rangeFormatted () {
if (null === this.selectedFrom || null === this.selectedTo) {
return '-';
updateSelectedRange() {
let range = [];
let currentDate = this.selectedFrom;
while (currentDate < this.selectedTo) {
range.push(currentDate);
currentDate = currentDate.add(1, 'day');
}
this.selectedRange = range;
},
onPaxUpdated() {
if (this.totalPax < 30) {
this.selectedBoard = null;
}
},
submitForm() {
let options = [];
for (let option of this.selectedOptions) {
options.push(option.uid);
}
let formData = {
'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name,
'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email,
'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone,
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.includesBonusCard ? '' : this.selectedPax,
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup1]': this.includesBonusCard ? this.selectedPaxGroup1 : '',
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup2]': this.includesBonusCard ? this.selectedPaxGroup2 : '',
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup3]': this.includesBonusCard ? this.selectedPaxGroup3 : '',
'tx_epproducts_ajax[groupsPriceInquiry][board]': this.selectedBoard ? this.selectedBoard.uid : null,
'tx_epproducts_ajax[groupsPriceInquiry][options]': options,
'tx_epproducts_ajax[groupsPriceInquiry][summary]': this.priceSummary
};
this.processing = true;
this.submitted = false;
axios({
url: this.endpoint,
method: 'POST',
data: $.param(formData)
}).then(response => {
if ('validation' === response.data.status) {
this.formErrors = response.data.errors;
} else {
this.submitted = true;
}
}).catch(error => {
}).finally(() => {
this.processing = false;
});
}
},
computed: {
nightsCount() {
return this.selectedRange.length;
},
rangeFormatted() {
if (null === this.selectedFrom || null === this.selectedTo) {
return '-';
}
return this.selectedFrom.format('DD.MM.YYYY') + ' - ' + this.selectedTo.format('DD.MM.YYYY');
},
pricePax () {
let base = 0;
let additional = 0;
for (let date of this.selectedRange) {
for (let config of this.configs) {
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
base += config.price;
if (this.selectedPax > config.personsIncluded) {
return this.selectedFrom.format('DD.MM.YYYY') + ' - ' + this.selectedTo.format('DD.MM.YYYY');
},
includesBonusCard() {
let included = false
for (let date of this.selectedRange) {
for (let config of this.configs) {
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
if (config.bonusCardIncluded) {
included = true
}
}
}
}
return included
},
pricePax() {
let base = 0
let additional = 0
for (let date of this.selectedRange) {
for (let config of this.configs) {
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
base += config.price
if (this.totalPax > config.personsIncluded) {
if (false === config.bonusCardIncluded) {
additional += (this.selectedPax - config.personsIncluded) * config.priceAdditionalPerson;
} else {
// Distribute additional persons costs by group
let included = config.personsIncluded
if (this.selectedPaxGroup3 <= included) {
included -= this.selectedPaxGroup3
} else {
additional += (this.selectedPaxGroup3 - included) * config.priceAdditionalPersonByGroup[3]
included = 0
}
if (this.selectedPaxGroup2 <= included) {
included -= this.selectedPaxGroup2
} else {
additional += (this.selectedPaxGroup2 - included) * config.priceAdditionalPersonByGroup[2]
included = 0
}
if (this.selectedPaxGroup1 > included) {
additional += (this.selectedPaxGroup1 - included) * config.priceAdditionalPersonByGroup[1]
}
}
}
}
}
return { base, additional };
},
priceOptions () {
let price = 0;
for (let option of this.selectedOptions) {
if (true === option.ignoreWithBoard && this.selectedBoard) {
continue;
}
if (1 === option.type) {
price += option.price;
} else if (2 === option.type) {
price += option.price * this.selectedPax;
} else if (3 === option.type) {
price += option.price * this.nightsCount;
} else if (4 === option.type) {
price += option.price * this.nightsCount * this.selectedPax;
}
}
return {base, additional};
},
totalPax() {
return this.includesBonusCard ? this.selectedPaxGroup1 + this.selectedPaxGroup2 + this.selectedPaxGroup3 : this.selectedPax
},
priceOptions() {
let price = 0;
for (let option of this.selectedOptions) {
if (true === option.ignoreWithBoard && this.selectedBoard) {
continue;
}
return price;
},
priceBoard () {
let price = 0;
if (this.selectedBoard) {
price = this.selectedBoard.price * this.nightsCount * this.selectedPax;
}
return price;
},
priceShortTerm () {
let price = 0;
const totalPricePax = this.pricePax.base + this.pricePax.additional;
if (2 === this.nightsCount) {
price = totalPricePax * 0.2;
}
if (3 === this.nightsCount) {
price = totalPricePax * 0.1;
}
return price;
},
priceRunningCosts () {
if (this.selectedBoard) {
return 0;
}
return this.nightsCount * this.selectedPax * 1.7;
},
priceTotal () {
return this.pricePax.base + this.pricePax.additional + this.priceOptions
+ this.priceBoard + this.priceShortTerm + this.priceRunningCosts;
},
priceSummary () {
return {
paxBase: this.formatCurrency(this.pricePax.base),
paxAdditional: this.formatCurrency(this.pricePax.additional),
shortTerm: this.formatCurrency(this.priceShortTerm),
options: this.formatCurrency(this.priceOptions),
board: this.formatCurrency(this.priceBoard),
runningCosts: this.formatCurrency(this.priceRunningCosts),
total: this.formatCurrency(this.priceTotal)
if (1 === option.type) {
price += option.price;
} else if (2 === option.type) {
price += option.price * this.totalPax;
} else if (3 === option.type) {
price += option.price * this.nightsCount;
} else if (4 === option.type) {
price += option.price * this.nightsCount * this.totalPax;
}
}
return price;
},
mounted () {
Vue.nextTick(() => {
this.picker = $(this.$refs.picker).flatpickr({
mode: 'range',
dateFormat: 'd.m.Y',
locale: German,
onChange: selectedDates => {
if (2 === selectedDates.length) {
this.selectedFrom = dayjs(selectedDates[0]);
this.selectedTo = dayjs(selectedDates[1]);
this.updateSelectedRange();
}
}
});
this.initSelectableRanges();
});
priceBoard() {
let price = 0;
if (this.selectedBoard) {
price = this.selectedBoard.price * this.nightsCount * this.totalPax;
}
return price;
},
priceShortTerm() {
let price = 0;
const totalPricePax = (this.pricePax.base + this.pricePax.additional);
for (let [nights, factor] of Object.entries(this.shortTermFactors)) {
if (nights === this.nightsCount) {
price = totalPricePax * factor;
}
}
return price;
},
priceRunningCosts() {
if (this.selectedBoard) {
return 0;
}
return this.nightsCount * this.totalPax * this.runningCostsFactor;
},
priceTotal() {
return this.pricePax.base + this.pricePax.additional + this.priceOptions
+ this.priceBoard + this.priceShortTerm + this.priceRunningCosts;
},
priceSummary() {
return {
paxBase: this.formatCurrency(this.pricePax.base),
paxAdditional: this.formatCurrency(this.pricePax.additional),
shortTerm: this.formatCurrency(this.priceShortTerm),
options: this.formatCurrency(this.priceOptions),
board: this.formatCurrency(this.priceBoard),
runningCosts: this.formatCurrency(this.priceRunningCosts),
total: this.formatCurrency(this.priceTotal)
}
}
},
mounted() {
Vue.nextTick(() => {
this.picker = $(this.$refs.picker).flatpickr({
mode: 'range',
dateFormat: 'd.m.Y',
locale: German,
onChange: selectedDates => {
if (2 === selectedDates.length) {
this.selectedFrom = dayjs(selectedDates[0]);
this.selectedTo = dayjs(selectedDates[1]);
this.updateSelectedRange();
}
}
});
this.initSelectableRanges();
});
}
}
</script>
<style scoped lang="scss">
.form-wrapper {
position: relative;
}
.form-control[readonly] {
background-color: white;
}
.form-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(white, 0.85);
}
.form-wrapper {
position: relative;
}
.form-control[readonly] {
background-color: white;
}
.form-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(white, 0.85);
}
</style>
@@ -101,6 +101,16 @@
<th>Beförderung</th>
<td>{{ getSelectedServicesLabels(participant.transportationIds, services.transportation) }}</td>
</tr>
<tr v-if="participant.surcharges.length > 0">
<th>Zuschläge</th>
<td>
<ul class="list-none p-0 mb-0">
<li v-for="surcharge in participant.surcharges">
{{ surcharge.priceSingle.toFixed(2) }} <span v-show="surcharge.name">({{ surcharge.name }})</span>
</li>
</ul>
</td>
</tr>
<tr>
<th>Gesamtpreis</th>
<td>{{ calculateSinglePrice(participant).toFixed(2) }} </td>
@@ -307,6 +317,9 @@
if ('S' === participant.data.status && participant.data.cancellationFee) {
singlePrice += participant.data.cancellationFee;
}
for (let surcharge of participant.surcharges) {
singlePrice += surcharge.priceSingle
}
return singlePrice;
},
onAccommodationSelected (accommodation, index) {
@@ -1,5 +1,5 @@
<template>
<div class="relative" v-if="mutable">
<div class="relative" v-if="mutable && selectableAccommodations.length > 0">
<button class="btn btn-primary btn-sm w-full lg:w-auto" type="button" @click.prevent="visible = !visible">
Zimmer <span class="glyphicon" :class="{ 'glyphicon-chevron-up': visible, 'glyphicon-chevron-down': !visible }"></span>
</button>
@@ -25,7 +25,7 @@
},
props: {
contingents: {
type: Object,
type: Array,
required: true
}
}
@@ -5,3 +5,7 @@
max-width: 165px;
margin: 0 auto;
}
.headline {
text-transform: none;
}
@@ -44,6 +44,9 @@
<trans-unit id="tx_eptheme.label.teaserbutton.product_no_booking">
<target>Details</target>
</trans-unit>
<trans-unit id="tx_eptheme.label.to_inquiry_form">
<target>Direkt zum Anfrageformular</target>
</trans-unit>
<trans-unit id="tx_eptheme_domain_model_lineup">
<target>Lineup</target>
</trans-unit>
@@ -43,6 +43,9 @@
<trans-unit id="tx_eptheme.label.teaserbutton.product_no_booking">
<source>Details</source>
</trans-unit>
<trans-unit id="tx_eptheme.label.to_inquiry_form">
<source>Inquiry Form</source>
</trans-unit>
<trans-unit id="tx_eptheme_domain_model_lineup">
<source>Lineup</source>
</trans-unit>
@@ -1,6 +1,13 @@
<f:if condition="{facts -> f:count()} > 0">
<div class="ep-sidebar ep-facts">
<p><strong>Facts</strong></p>
<f:if condition="{title}">
<f:then>
<h2 class="headline headline--4">Skiurlaub {title} Facts</h2>
</f:then>
<f:else>
<p><strong>Facts</strong></p>
</f:else>
</f:if>
<div>
<ul class="ep-facts">
<f:for each="{facts}" as="fact">
@@ -1,6 +1,7 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true">
<formvh:renderRenderable renderable="{element}">
<f:render partial="Field/Field" arguments="{element: element, doNotShowLabel: 1}" contentAs="elementContent">
<span class="block text-gray-700 mb-1">{formvh:translateElementProperty(element: element, property: 'label')}<f:if condition="{element.required}"><f:render partial="Field/Required" /></f:if></span>
<f:for each="{element.properties.options}" as="label" key="value" iteration="idIterator">
<div>
<label for="{element.uniqueIdentifier}-{idIterator.index}" class="inline-flex items-center">
@@ -17,35 +17,46 @@
<div class="region-details region-details--ski-area" data-rte-content>
{region.skiArea -> f:format.html()}
<f:if condition="{teasers}">
<a id="teasers" class="anchor"></a>
<div class="slider slider--teasers in-column">
<div class="row slider__items">
<f:for each="{teasers}" as="teaser">
<div class="col-md-4">
<f:render partial="Product/Teaser" section="Teaser" arguments="{teaser: teaser}"/>
</div>
</f:for>
<f:then>
<a id="teasers" class="anchor"></a>
<div class="slider slider--teasers in-column">
<div class="row slider__items">
<f:for each="{teasers}" as="teaser">
<div class="col-md-4">
<f:render partial="Product/Teaser" section="Teaser" arguments="{teaser: teaser}"/>
</div>
</f:for>
</div>
</div>
</div>
<f:if condition="{settings.showMoreLink}">
<p class="text-right">
<f:link.action pageUid="{settings.defaultStaticSearchPageUid}" class="button"
action="staticresult" controller="Search"
arguments="{encodedFilterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'}"
additionalAttributes="{rel: 'nofollow'}">
Alle anzeigen
</f:link.action>
</p>
</f:if>
<f:if condition="{settings.showMoreLink}">
<p class="text-right">
<f:link.action pageUid="{settings.defaultStaticSearchPageUid}" class="button"
action="staticresult" controller="Search"
arguments="{encodedFilterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'}"
additionalAttributes="{rel: 'nofollow'}">
Alle anzeigen
</f:link.action>
</p>
</f:if>
</f:then>
<f:else>
<f:comment><!-- Show image gallery at teasers' position in case there are none --></f:comment>
<f:if condition="{region.images -> f:count()} > 0">
<f:render partial="ContentSlider" arguments="{images: region.images, id: 'region-{region.uid}', thumbnails: 1}"/>
</f:if>
</f:else>
</f:if>
<f:if condition="{region.skiAreaExtended}">{region.skiAreaExtended -> f:format.html()}</f:if>
</div>
<f:if condition="{region.images -> f:count()} > 0">
<f:render partial="ContentSlider" arguments="{images: region.images, id: 'region-{region.uid}', thumbnails: 1}"/>
<f:if condition="{teasers}">
<f:comment><!-- Show image gallery unless there are no teasers --></f:comment>
<f:if condition="{region.images -> f:count()} > 0">
<f:render partial="ContentSlider" arguments="{images: region.images, id: 'region-{region.uid}', thumbnails: 1}"/>
</f:if>
</f:if>
<f:if condition="{bars}">
<f:then><div class="ep-headbar">{f:if(condition: '{region.season} == "w"', then: 'Skipass', else: 'Highlights')}</div></f:then>
<f:else><h2>{f:if(condition: '{region.season} == "w"', then: 'Skipass', else: 'Highlights')}</h2></f:else>
<f:then><div class="ep-headbar">{f:if(condition: '{region.season} == "w"', then: 'Skipass', else: 'Highlights')} {region.name}</div></f:then>
<f:else><h3>{f:if(condition: '{region.season} == "w"', then: 'Skipass', else: 'Highlights')} {region.name}</h3></f:else>
</f:if>
<f:if condition="{region.skiPass}">
<div class="region-details region-details--skipass" data-rte-content>
@@ -60,7 +71,7 @@
<f:if condition="{region.news}">
<f:if condition="{bars}">
<f:then><div class="ep-headbar">Aktuell</div></f:then>
<f:else><h2>Aktuell</h2></f:else>
<f:else><h3>Aktuelles {region.name}</h3></f:else>
</f:if>
<div class="region-details region-details--news" data-rte-content>
{region.news -> f:format.html()}
@@ -1,40 +1,30 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<f:if condition="{data.header_link}">
<v:condition.string.isNumeric value="{data.header_link}">
<f:then>
<f:link.typolink parameter="{data.header_link}" title="{data.header}" class="teaserbox teaserbox--default">
<f:render section="Content" arguments="{_all}"/>
</f:link.typolink>
</f:then>
<f:else>
<a href="{data.header_link}" target="_blank" title="{data.header}" class="teaserbox teaserbox--default">
<f:render section="Content" arguments="{_all}"/>
</a>
</f:else>
</v:condition.string.isNumeric>
</f:if>
</f:section>
<f:section name="Content">
<div class="teaserbox__image">
<f:if condition="{context.country}">
<f:image class="teaserbox__flag"
src="{ep:flagiconSource(country: context.country)}" alt="{context.country.name}"/>
</f:if>
<f:render partial="TeaserImage" arguments="{image: teaserImage}"/>
</div>
<div class="teaserbox__main" data-rte-content>
<span class="headline headline--3 headline--teaser">{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}</span>
<hr>
{data.bodytext -> f:format.html()}
</div>
<button class="button button--full">{data.button -> v:or(alternative: 'Details')}</button>
<f:link.typolink parameter="{data.header_link}"
class="teaserbox teaserbox--default">
<div class="teaserbox__image">
<f:if condition="{context.country}">
<f:image class="teaserbox__flag"
src="{ep:flagiconSource(country: context.country)}"
alt="{context.country.name}"/>
</f:if>
<f:render partial="TeaserImage" arguments="{image: teaserImage}"/>
</div>
<div class="teaserbox__main" data-rte-content>
<span class="headline headline--3 headline--teaser">
{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}
</span>
<hr>
{data.bodytext -> f:format.html()}
</div>
<button class="button w-full">
{data.button -> v:or(alternative: 'Details')}
</button>
</f:link.typolink>
</f:section>
</html>
@@ -3,35 +3,23 @@
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Teaser">
<f:if condition="{data.header_link}">
<v:condition.string.isNumeric value="{data.header_link}">
<f:then>
<f:link.typolink parameter="{data.header_link}" title="{data.header}" class="teaserbox teaserbox--default">
<f:render section="Content" arguments="{_all}"/>
</f:link.typolink>
</f:then>
<f:else>
<a href="{data.header_link}" target="_blank" title="{data.header}" class="teaserbox teaserbox--default">
<f:render section="Content" arguments="{_all}"/>
</a>
</f:else>
</v:condition.string.isNumeric>
</f:if>
</f:section>
<f:section name="Content">
<div class="teasebox__image">
<f:render partial="TeaserImage" arguments="{image: teaserImage}"/>
</div>
<div class="teaserbox__main" data-rte-content>
<span class="headline headline--3 headline--teaser">{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}</span>
<hr>
{data.bodytext -> f:format.html()}
</div>
<button class="button button--full">{data.button -> v:or(alternative: 'Details')}</button>
<f:link.typolink parameter="{data.header_link}"
class="teaserbox teaserbox--default">
<div class="teasebox__image">
<f:render partial="TeaserImage" arguments="{image: teaserImage}"/>
</div>
<div class="teaserbox__main" data-rte-content>
<span class="headline headline--3 headline--teaser">
{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}
</span>
<hr>
{data.bodytext -> f:format.html()}
</div>
<button class="button button--full">
{data.button -> v:or(alternative: 'Details')}
</button>
</f:link.typolink>
</f:section>
</html>
@@ -30,7 +30,7 @@
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
<p class="hidden-xs hidden-md"><a @click.prevent="scrollTo('#teasers')" href="#teasers" class="button button--full">Direkt zu den Angeboten</a></p>
</f:if>
<f:render partial="Facts" arguments="{facts: city.facts}"/>
<f:render partial="Facts" arguments="{facts: city.facts, title: city.name}"/>
<f:if condition="{city.region.season} == 'w'">
<f:render partial="Snowreport" arguments="{snowreport: city.region.snowreport}"/>
<f:render partial="Regioninfo" arguments="{region: city.region}"/>
@@ -58,7 +58,9 @@
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
<p class="hidden-xs hidden-md"><a @click.prevent="scrollTo('#teasers')" href="#teasers" class="button button--full">Direkt zu den Angeboten</a></p>
</f:if>
<f:render partial="Facts" arguments="{facts: country.facts}"/>
<f:render partial="Facts" arguments="{facts: country.facts, title: country.name}"/>
<f:render partial="Officetip" arguments="{officetip: country.officetip}"/>
<f:render partial="Snowreport" arguments="{snowreport: country.snowreport}"/>
</div>
</div>
</div>
@@ -29,7 +29,19 @@
</tr>
<tr>
<th>Anzahl der Personen</th>
<td>{pax}</td>
<td>{pax -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (ab 15 Jahre)</th>
<td>{paxGroup3 -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (7-14 Jahre)</th>
<td>{paxGroup2 -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (4-6 Jahre)</th>
<td>{paxGroup1 -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Verpflegung</th>
@@ -63,6 +75,7 @@
<td>
Grundpreis: {summary.paxBase}<br>
Aufpreis Personen: {summary.paxAdditional -> v:or(alternative: '-')}<br>
Aufpreis Kurzzeit: {summary.shortTerm -> v:or(alternative: '-')}<br>
Zusatzleistungen: {summary.options}<br>
Strom- und Abfallgebühren: {summary.runningCosts -> v:or(alternative: '-')}<br>
<strong>Gesamtpreis: {summary.total}</strong>
@@ -31,7 +31,7 @@
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
<p class="hidden-xs hidden-md"><a @click.prevent="scrollTo('#teasers')" href="#teasers" class="button button--full">Direkt zu den Angeboten</a></p>
</f:if>
<f:render partial="Facts" arguments="{facts: hotel.facts}"/>
<f:render partial="Facts" arguments="{facts: hotel.facts, title: hotel.name}"/>
<f:if condition="{hotel.region.season} == 'w'">
<f:render partial="Snowreport" arguments="{snowreport: hotel.region.snowreport}"/>
<f:render partial="Regioninfo" arguments="{region: hotel.region}"/>
@@ -282,7 +282,7 @@
<f:if condition="{hotel.groupsPriceConfigs}">
<f:else>
<f:link.typolink class="button button--full" parameter="{settings.groupsContactFormPageUid}" additionalParams="&ref={currentPageUid}">
Direkt zum Anfrageformular
<f:translate key="tx_eptheme.label.to_inquiry_form" extensionName="ep_theme"/>
</f:link.typolink>
</f:else>
</f:if>
@@ -37,7 +37,7 @@
</p>
</f:if>
<f:if condition="{region.season} == 'w'">
<f:render partial="Facts" arguments="{facts: region.facts}"/>
<f:render partial="Facts" arguments="{facts: region.facts, title: region.name}"/>
<f:render partial="Snowreport" arguments="{snowreport: region.snowreport}"/>
<f:render partial="Regioninfo" arguments="{region: region}"/>
<f:render partial="Region/Webcam" arguments="{region: region}"/>
@@ -97,9 +97,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
@@ -115,9 +112,6 @@ return [
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => [
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],