Compare commits

...
38 Commits
Author SHA1 Message Date
frommeandClaude Opus 5.5 3f88229a70 feat: settings.php is shared on the server and no longer in git; production is leading
Every deploy replaced settings.php with the committed version, so changes
made in the production backend (extension configuration, install tool) were
lost with the next deploy or rollback. As LocalConfiguration.php on TYPO3 10,
it is now a shared file. typo3:settings:init seeds it once from the live
release; deploy:shared would otherwise seed it from the new release, i.e.
from git, and drop production's settings one last time.

The file is no longer tracked: it holds the encryptionKey and the install
tool password hash, and git has no use for it once production is leading.
Local copies stay in place (ignored).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 20:31:21 +02:00
fromme 6edd0fab6e feat: add logo in png format for email templates 2026-10-24 20:08:43 +02:00
frommeandClaude Opus 5.5 3c27cd84b9 fix: file names stay ASCII as on TYPO3 10 (UTF8filesystem off)
The fresh TYPO3 13 installation got UTF8filesystem = true from the core's
factory configuration; TYPO3 10 ran with the default false, so uploads
turned umlauts into ASCII (Bärenhütte.jpg -> Baerenhuette.jpg) and no file
in sys_file has a non-ASCII name. With true and no UTF-8 system locale,
escapeshellarg() drops those characters and image processing misses the
file (environment check warning). Back to false: the TYPO3 10 behaviour,
no locale needed, and file URLs and CDN paths stay free of encoded umlauts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 19:57:02 +02:00
frommeandClaude Opus 5.5 283f54c56c feat: EXT:staticfilecache serves cached pages without PHP again, as on TYPO3 10
Every page view goes through TYPO3 13 since the switch; staticfilecache
15.2 writes a gzip copy of each cacheable page, and the TYPO3 10 rewrite
rules in public/.htaccess let Apache deliver it directly (GET, no query
string, no backend login). Pages answer in about 2 ms instead of
0.5-4 s; static and dynamic output are identical for a sample of 22
sitemap URLs.

A flush of the pages cache group (travel data and snow report imports,
"clear frontend caches", deploys) removes the files; a page's own cache
lifetime still applies through the generated per-page .htaccess.

X-SFC-Tags is not passed on: with every record of a page as a tag it
reaches 100+ KB, more than Apache accepts from PHP-FPM, and it would
expose internal record ids. Debug headers stay off for the same reason.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 19:05:21 +02:00
frommeandClaude Opus 5.5 df3d823de4 feat: page, hash and rootline caches in Redis again, as on TYPO3 10
Since every page view goes through TYPO3 13, the page cache is on the hot
path; TYPO3 10 kept it in Redis, TYPO3 13 had it in MySQL. Same socket and
options as TYPO3 10, but databases 3-5 instead of 0-2, which may still
hold TYPO3 10 entries in a format TYPO3 13 must not read. DDEV gets the
redis add-on for the same setup locally.

Without a Redis connection the backend logs an alert and misses, so pages
still render (slower); a missing PHP redis extension, however, fails hard.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 18:51:57 +02:00
frommeandClaude Opus 5.5 520feb003f feat: scheduler runs the imports and the housekeeping tasks again
typo3/cms-scheduler 13.4 replaces the three direct cron jobs with one
scheduler:run cron job. ep:scheduler-tasks (one-off, idempotent) soft-deletes
the TYPO3 10 tasks TYPO3 13 cannot run (import task classes that are gone, one
groups.swiss task per house with the API keys in the database, the retired
E&P Events log) and adds "Execute console commands" tasks for ep:import-dates,
ep:import-snowreport and ep:groups-swiss with the TYPO3 10 schedules. The core
garbage collection, IP anonymization and file indexing tasks keep working as
they are; tx_epproducts_log is registered for garbage collection again.

The first execution is the next cron match, so adding the tasks never starts
an import or a groups.swiss export right away.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 18:27:50 +02:00
frommeandClaude Opus 5.5 f193d356cb feat: schneereisen.com redirects to www.ep-reisen.de
An alias of the ep-reisen vhost; path kept, 301.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 15:24:13 +02:00
frommeandClaude Opus 5.5 fcf04dbca7 chore: no delayed cache flush after deploys
Waiting 150 s at the end of every deploy is impractical. The flush right
after the switch stays; the window in which the previous release can
still fill the page cache is documented in deploy.php.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 15:03:23 +02:00
frommeandClaude Opus 5.5 837fca3b4c feat: unisnow.de and unisnow.com redirect to snowbreak.ep-reisen.de
Like unichamp.de: path kept, 302.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:58:09 +02:00
frommeandClaude Opus 5.5 fec781d634 feat: ep-reisen.com and ep-reisen.info redirect to www.ep-reisen.de
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:57:29 +02:00
frommeandClaude Opus 5.5 cd7d347006 feat: every ep-events.de URL redirects to the company trips page
E&P Events is retired; www and bare domain go to
www.ep-reisen.de/reisen-fuer-gruppen/gruppenreisen/firmenreisen/ (301).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:54:36 +02:00
frommeandClaude Opus 5.5 4b6dfdd390 fix: skiwochenenden.de redirects to www.ski-wochenenden.de
An alias of the ski-wochenenden vhost that the static docroot answered
regardless of the host; TYPO3 13 rejected it (trusted hosts pattern).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:50:47 +02:00
frommeandClaude Opus 5.5 4bfa0f258a fix: deploys flush the TYPO3 caches again after PHP has picked up the release
A vhost on current/public (alvac since today) keeps running the previous
release until PHP's realpath cache expires (120 s by default); pages it
rendered after the first flush stayed in the page cache with old paths.
A second flush 150 s after the switch (also after a rollback) removes them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:45:36 +02:00
frommeandClaude Opus 5.5 419ffe42b8 fix: TYPO3 caches are flushed after a rollback too
Pages cached while the other release was live survived the switch
(seen after the rollback test: alvac served old favicon paths until a
manual cache:flush). The flush task runs in current_path, which is the
new release after a deploy and the previous one after a rollback.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:44:08 +02:00
frommeandClaude Opus 5.5 873579d5b0 feat: legacy domain and /buchung redirects in TYPO3 13's .htaccess
Ported unchanged from TYPO3 10's public/.htaccess, whose rules the static
docroots carry until the vhosts point at TYPO3 13; Force SSL as there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:28:47 +02:00
frommeandClaude Opus 5.5 29f39fb440 fix: the release links fileadmin with a relative symlink
The vhosts answer 403 for files behind the absolute symlink (tested on
staging); Deployer's shared dirs are relative and work. The task now
also checks that the link resolves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:23:26 +02:00
frommeandClaude Opus 5.5 e733a53935 fix: favicons and the schema.org logo use the published resource path
They pointed to /typo3conf/ext/..., which only exists in the static
docroots.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:54:57 +02:00
fromme 80fe3100ae Revert "fix: public resources answer under their TYPO3 10 path"
This reverts commit 9b90e32e6c.
2026-10-24 13:50:36 +02:00
frommeandClaude Opus 5.5 9b90e32e6c fix: public resources answer under their TYPO3 10 path
/typo3conf/ext/ep_theme|ep_products/Resources/Public/... is rewritten to
the _assets folder Composer publishes them to: the favicons and the
schema.org logo use that path, and so do links from outside. Today the
static docroots carry copies; after the vhost switch it would be 404.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:49:26 +02:00
frommeandClaude Opus 5.5 d7374c697d fix: blog pagination no longer highlights the link to page 1
The TYPO3 10 paginator styled it bg-button--secondary, which looked like
the current page.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:47:06 +02:00
frommeandClaude Opus 5.5 99f9724574 feat: backend users see the TYPO3 13 frontend on staging.ep-reisen.de
Logged in to the staging backend (be_typo_user cookie of that host), the
frontend is no longer redirected to www, so pages can be checked before
they go live. Everyone else still gets the 301; the host sends
X-Robots-Tag: noindex.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:24:19 +02:00
frommeandClaude Opus 5.5 b48cbcd430 fix: new content element wizard offers only element types TYPO3 13 has
Occupancy calendar, event price table and deal of the week were not
ported (no visible uses), the newsletter form is retired.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:03:38 +02:00
frommeandClaude Opus 5.5 bf171285cd feat: FAQ overview element on TYPO3 13
Restored from a46918d (the FAQ page /skireisen/infos/allgemeine-infos/faqs/);
markup matches TYPO3 10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 13:03:38 +02:00
frommeandClaude Opus 5.5 0cfd856894 chore: EXT:news default configuration
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:56:51 +02:00
frommeandClaude Opus 5.5 0b17a24edd feat: page module previews for teasers, disrupter, disturber, map and lineup
The TYPO3 10 drawItem hooks become one preview renderer registered per
CType in TCA; it renders the old templates from a46918d with the same
variables (f:format.html is not allowed in the backend: f:sanitize.html).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:56:22 +02:00
frommeandClaude Opus 5.5 44614a4a75 feat: blog on TYPO3 13 with EXT:news 14
News list, tag list and detail templates restored from a46918d; the
pagination widget is gone in news 14, so the TYPO3 10 paginator markup
now sits on the core sliding window pagination (pagination URLs change).
German labels for news committed. Production needs the news upgrade
wizards (the 5 plugin elements become the split news plugins).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:40:06 +02:00
frommeandClaude Opus 5.5 799dc03c75 feat: retire the group price calculator
MyE&P replaced it; TYPO3 only renders the price calendar. The element
stays in the database and renders nothing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:35:28 +02:00
frommeandClaude Opus 5.5 ad4089ae16 feat: lineup element on TYPO3 13
Restored from a46918d, TCA ported to TYPO3 13; markup matches TYPO3 10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:29:07 +02:00
frommeandClaude Opus 5.5 a042eff41d feat: hotel, region, city, country and concept plugins, FAQ, team, maps and price table on TYPO3 13
Controllers, templates and FlexForms restored from a46918d, ported with
Rector/Fractor and by hand (settings and rows missing from older
records read as empty, as PHP 7.4 did). They stay list_type plugins for
now. The pages match TYPO3 10 apart from data that changed since (dates,
prices, edited texts).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:27:26 +02:00
frommeandClaude Opus 5.5 53faeccb70 feat: front page, event front page and popup page layouts on TYPO3 13
Restored from a46918d unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:05:01 +02:00
frommeandClaude Opus 5.5 2999abc16c feat: contact, text with image, YouTube/Vimeo video, disrupter and map elements on TYPO3 13
Restored from a46918d, TCA ported to TYPO3 13. Their markup matches
TYPO3 10 on every page that renders. Headers with the default layout
stay hidden, as on TYPO3 10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:05:01 +02:00
frommeandClaude Opus 5.5 49b96df21a feat: retire the newsletter form, newsletter subscription and reseller export
The elements stay in the database and render nothing, so their pages no
longer fail on TYPO3 13.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 11:52:34 +02:00
frommeandClaude Opus 5.5 9668f08b4c chore: X-Ep-Compare header renders failing elements as placeholders in development
Lets the TYPO3 10 comparisons check elements on pages that still hold
content types without a rendering definition.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 11:51:55 +02:00
frommeandClaude Opus 5.5 301e89e60c feat: teasers and the left/event sidebar page layouts on TYPO3 13
Templates restored from a46918d unchanged; TCA and TypoScript were
already there. The teaser markup matches TYPO3 10 on every page that
renders (image titles lose trailing whitespace).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 11:51:55 +02:00
frommeandClaude Opus 5.5 dfed542af1 chore: remove the gridelements migration and the address check table
The grids are migrated in production, and the production database is the
master that is never replaced by a TYPO3 10 dump, so ep:migrate-gridelements
has nothing left to do; its tx_gridelements_* columns are dropped. The
address check forms are gone (pages removed), and so is the table that
stored their submissions: personal data that has not been processed since
2019.

The tables and columns themselves are dropped by hand: extension:setup only
adds and changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 18:29:29 +02:00
frommeandClaude Opus 5.5 d86efd72e3 fix: ep:migrate-gridelements stops cleanly when the report cannot be written
An unwritable --report path ended in a TypeError from fputcsv() after the
counts were printed. The command now says so and exits with an error before
anything is migrated. Page titles in the report include hidden pages
(Connection::select() applied the frontend restrictions).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 18:07:05 +02:00
frommeandClaude Opus 5.5 119364dcaa fix: reference index update no longer fails on the job application forms
referenceindex:update stopped at tt_content with "The Prototype
applicationform was not found": the backend and the CLI (form editor,
FlexForm of the form plugin, reference index) read module.tx_form, and the
form setup was only registered for the frontend (plugin.tx_form). TYPO3 10
set module.tx_form in the site TypoScript; TYPO3 13 reads it only from
global TypoScript, so ext_localconf.php registers FormSetup.yaml there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 17:47:57 +02:00
frommeandClaude Opus 5.5 4172ed978d feat: gridelements grids become container elements
TYPO3 13 does not know gridelements_pi1, so grids rendered nothing: the
inquiry form on /haus-christiler/ was missing. The five grid layouts are
container CTypes now (ep_container_halves, _thirds, _columns_quarters,
_half_and_two_quarters, _two_quarters_and_half; colPos 201-243) with the
TYPO3 10 templates ported. TwoQuartersAndHalf had no setup and output the
wrong columns; it renders its own columns now. As before, no container
inside these columns and the column rules of the old TSconfig apply.

ep:migrate-gridelements converts the data in place (plain SQL, idempotent,
--dry-run and --report): grids get their container CType, children in a
valid column move into it. Children in a column their layout lacks and
leftovers whose parent is no grid any more were invisible in TYPO3 10 and
are soft deleted. TYPO3 10 did not apply defaultHeaderType inside grids,
so text/textmedia/textpic children with header layout "default" get
"hidden" to keep their headers invisible.

Checked against TYPO3 10 on the 101 pages with grids that TYPO3 13 renders:
the container markup is identical; remaining differences are inside not
yet ported child elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 17:41:40 +02:00
159 changed files with 8032 additions and 338 deletions

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
name: redis
repository: ddev/ddev-redis
version: v3.0.0
install_date: "2026-10-24T18:50:06+02:00"
project_files:
- docker-compose.redis.yaml
- redis/scripts/settings.ddev.redis.php
- redis/scripts/setup-drupal-settings.sh
- redis/scripts/setup-redis-optimized-config.sh
- redis/redis.conf
- redis/advanced.conf
- redis/append.conf
- redis/general.conf
- redis/io.conf
- redis/memory.conf
- redis/network.conf
- redis/security.conf
- redis/snapshots.conf
- commands/host/redis-backend
- commands/redis/redis-cli
- commands/redis/redis-flush
global_files: []
removal_actions:
- |
#ddev-description:Remove redis settings if applicable
files=(
"${DDEV_APPROOT}/${DDEV_DOCROOT}/sites/default/settings.ddev.redis.php"
"${DDEV_APPROOT}/.ddev/docker-compose.redis_extra.yaml"
)
for file in "${files[@]}"; do
if [ -f "$file" ]; then
if grep -q '#ddev-generated' "$file"; then
rm -f "$file"
else
echo "Unwilling to remove '$file' because it does not have #ddev-generated in it; you can manually delete it if it is safe to delete."
fi
fi
done
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
#ddev-generated
## Description: Use a different key-value store for Redis
## Usage: redis-backend <image> [optimize]
## Example: ddev redis-backend redis-alpine optimize
# Keep in sync with the default image in docker-compose.redis.yaml
DEFAULT_REDIS_DOCKER_IMAGE="redis:8"
REDIS_DOCKER_IMAGE=${1:-}
REDIS_CONFIG=${2:-}
function show_help() {
cat <<EOF
Usage: ddev redis-backend <image|alias> [optimize]
Choose from predefined aliases, or provide any Redis-compatible Docker image.
Note that not every Docker image can work right away, and you may need to override
the "command:" in the docker-compose.redis_extra.yaml file
Available aliases:
redis ${DEFAULT_REDIS_DOCKER_IMAGE}
redis-alpine ${DEFAULT_REDIS_DOCKER_IMAGE}-alpine
valkey valkey/valkey:9
valkey-alpine valkey/valkey:9-alpine
Custom backend:
You can specify any Docker image, e.g.:
ddev redis-backend redis:7
Optional:
optimize Apply additional Redis configuration with resource limits
optimized Same as optimize
Examples:
ddev redis-backend redis-alpine optimize
ddev redis-backend valkey
ddev redis-backend redis:8.2-alpine
EOF
exit 0
}
function optimize_config() {
[[ "$REDIS_CONFIG" != "optimized" && "$REDIS_CONFIG" != "optimize" ]] && return
ddev dotenv set .ddev/.env.redis --redis-optimized=true
}
function change_hostname() {
[[ "${REDIS_HOSTNAME:-}" == "" ]] && return
ddev dotenv set .ddev/.env.redis --redis-hostname="$REDIS_HOSTNAME"
}
function cleanup() {
rm -f "$DDEV_APPROOT/.ddev/.env.redis"
rm -rf "$DDEV_APPROOT/.ddev/redis/"
rm -f "$DDEV_APPROOT/.ddev/docker-compose.redis.yaml" "$DDEV_APPROOT/.ddev/docker-compose.redis_extra.yaml"
redis_volume="ddev-$(ddev status -j | docker run -i --rm ddev/ddev-utilities jq -r '.raw.name')_redis"
if docker volume ls -q | grep -qw "$redis_volume"; then
ddev stop
docker volume rm "$redis_volume"
fi
}
function check_docker_image() {
echo "Pulling ${REDIS_DOCKER_IMAGE}..."
if ! docker pull "$REDIS_DOCKER_IMAGE"; then
echo >&2 "❌ Unable to pull ${REDIS_DOCKER_IMAGE}"
exit 2
fi
}
function use_docker_image() {
[[ "$REDIS_DOCKER_IMAGE" != "$DEFAULT_REDIS_DOCKER_IMAGE" ]] && ddev dotenv set .ddev/.env.redis --redis-docker-image="$REDIS_DOCKER_IMAGE"
# Reinstall the same add-on version that is installed now
local repo version flags=()
IFS='|' read -r repo version < <(ddev add-on list --installed -j 2>/dev/null | docker run -i --rm ddev/ddev-utilities jq -r '.raw[] | select(.Name=="redis") | "\(.Repository // "")|\(.Version // "")"' 2>/dev/null)
case "$version" in
# installed from a local directory
"") ;;
pr-*) flags=(--pr "${version#pr-}") ;;
v[0-9]*) flags=(--version "$version") ;;
# the default branch name, e.g. "main"
*) flags=(--default-branch) ;;
esac
ddev add-on get "${repo:-ddev/ddev-redis}" "${flags[@]}"
}
# "ddev add-on get" overwrites this file while it is running, so keep the main flow
# in a block, which bash reads completely before running it
{
case "$REDIS_DOCKER_IMAGE" in
redis)
REDIS_DOCKER_IMAGE="$DEFAULT_REDIS_DOCKER_IMAGE"
;;
redis-alpine)
REDIS_DOCKER_IMAGE="${DEFAULT_REDIS_DOCKER_IMAGE}-alpine"
;;
valkey)
REDIS_DOCKER_IMAGE="valkey/valkey:9"
REDIS_HOSTNAME="valkey"
;;
valkey-alpine)
REDIS_DOCKER_IMAGE="valkey/valkey:9-alpine"
REDIS_HOSTNAME="valkey"
;;
""|--help|-h)
show_help
;;
*)
# Allow unknown image, nothing to override
;;
esac
check_docker_image
cleanup
optimize_config
change_hostname
use_docker_image
echo
echo "✅ Redis backend: $REDIS_DOCKER_IMAGE"
if [[ "$REDIS_CONFIG" == "optimized" || "$REDIS_CONFIG" == "optimize" ]]; then
echo "⚙️ Redis config: optimized"
else
echo "⚙️ Redis config: default"
fi
echo
echo "📝 Commit the '.ddev' directory to version control"
echo
echo "🔄 Redis config available after 'ddev restart'"
exit 0
}
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env sh
#ddev-generated
## Description: Run redis-cli inside the Redis container
## Usage: redis-cli [flags] [args]
## Example: "ddev redis-cli KEYS *" or "ddev redis-cli INFO" or "ddev redis-cli --version"
## Aliases: redis
if [ -f /etc/redis/conf/security.conf ]; then
redis-cli -p 6379 -h "${REDIS_HOSTNAME:-redis}" -a redis --no-auth-warning $@
else
redis-cli -p 6379 -h "${REDIS_HOSTNAME:-redis}" $@
fi
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env sh
#ddev-generated
## Description: Flush all cache inside the Redis container
## Usage: redis-flush
## Example: "ddev redis-flush"
if [ -f /etc/redis/conf/security.conf ]; then
redis-cli -p 6379 -h "${REDIS_HOSTNAME:-redis}" -a redis --no-auth-warning FLUSHALL ASYNC
else
redis-cli -p 6379 -h "${REDIS_HOSTNAME:-redis}" FLUSHALL ASYNC
fi
+27
View File
@@ -0,0 +1,27 @@
#ddev-generated
services:
redis:
container_name: ddev-${DDEV_SITENAME}-redis
image: ${REDIS_DOCKER_IMAGE:-redis:8}
hostname: ${REDIS_HOSTNAME:-redis}
# These labels ensure this service is discoverable by ddev.
labels:
com.ddev.site-name: ${DDEV_SITENAME}
com.ddev.approot: ${DDEV_APPROOT}
restart: "no"
expose:
- 6379
volumes:
- ".:/mnt/ddev_config"
- "ddev-global-cache:/mnt/ddev-global-cache"
- "./redis:/etc/redis/conf"
- "redis:/data"
command: /etc/redis/conf/redis.conf
x-ddev:
describe-url-port: |
Backend: ${REDIS_DOCKER_IMAGE:-redis:8}
describe-info: |
Pass: <none>
volumes:
redis:
+13
View File
@@ -0,0 +1,13 @@
# Redis configuration.
# #ddev-generated
# Example configuration files for reference:
# http://download.redis.io/redis-stable/redis.conf
# http://download.redis.io/redis-stable/sentinel.conf
maxmemory 2048mb
maxmemory-policy allkeys-lfu
# to disable Redis persistence, remove ddev-generated from this file,
# and uncomment the two lines below:
#appendonly no
#save ""
+3
View File
@@ -19,6 +19,8 @@
"ep/ep-products": "@dev",
"ep/ep-theme": "@dev",
"fluidtypo3/vhs": "^8.0",
"georgringer/news": "^14.1",
"lochmueller/staticfilecache": "^15.2",
"ssch/typo3-encore": "^7.0",
"typo3/cms-backend": "^13.4",
"typo3/cms-belog": "^13.4",
@@ -41,6 +43,7 @@
"typo3/cms-recycler": "^13.4",
"typo3/cms-redirects": "^13.4",
"typo3/cms-rte-ckeditor": "^13.4",
"typo3/cms-scheduler": "^13.4",
"typo3/cms-seo": "^13.4",
"typo3/cms-setup": "^13.4",
"typo3/cms-sys-note": "^13.4",
Generated
+253 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "bcb472ad98cfa2a48699faed4f6366e8",
"content-hash": "f1134476988f3bf5d2a3a74092cdb285",
"packages": [
{
"name": "b13/container",
@@ -1134,6 +1134,115 @@
},
"time": "2026-07-23T10:57:34+00:00"
},
{
"name": "georgringer/news",
"version": "14.1.2",
"source": {
"type": "git",
"url": "https://github.com/georgringer/news.git",
"reference": "36a5a983022f5648352975142d9daef6f77a657d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/georgringer/news/zipball/36a5a983022f5648352975142d9daef6f77a657d",
"reference": "36a5a983022f5648352975142d9daef6f77a657d",
"shasum": ""
},
"require": {
"php": ">= 8.2 < 8.6",
"symfony/console": "^5.4 || ^6.4 || ^7.0",
"typo3/cms-backend": "^13.4.29 || ^14.3",
"typo3/cms-core": "^13.4.29 || ^14.3",
"typo3/cms-extbase": "^13.4.29 || ^14.3",
"typo3/cms-fluid": "^13.4.29 || ^14.3",
"typo3/cms-frontend": "^13.4.29 || ^14.3",
"typo3/cms-install": "^13.4.29 || ^14.3"
},
"conflict": {
"sebastian/recursion-context": "6.0.0 || 6.0.1 || 6.0.2"
},
"replace": {
"typo3-ter/news": "self.version"
},
"require-dev": {
"a9f/fractor": "~1.0.0",
"a9f/fractor-xliff": "~1.0.0",
"a9f/fractor-xml": "~1.0.0",
"a9f/typo3-fractor": "@dev",
"friendsofphp/php-cs-fixer": "^3.89.2",
"phpstan/phpdoc-parser": "^1.33.0",
"phpunit/phpunit": "^10.5.58",
"ssch/typo3-rector": "^3.7",
"typo3/cms-fluid-styled-content": "^13.4.29 || ^14.3",
"typo3/cms-seo": "^13.4.29 || ^14.3",
"typo3/coding-standards": "^0.8.0",
"typo3/testing-framework": "^8.3.1",
"webmozart/assert": "^1.12.1"
},
"suggest": {
"georgringer/news-tagsuggest": "On the fly creation of tag records within a news record",
"georgringer/numbered-pagination": "Improved pagination API",
"reelworx/rx-shariff": "GDPR compliant social sharing",
"typo3/cms-scheduler": "XML and ICS import",
"typo3/cms-seo": "Sitemaps"
},
"type": "typo3-cms-extension",
"extra": {
"typo3/cms": {
"app-dir": ".Build",
"web-dir": ".Build/Web",
"extension-key": "news"
}
},
"autoload": {
"psr-4": {
"GeorgRinger\\News\\": "Classes"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Georg Ringer",
"homepage": "https://ringer.it",
"role": "Developer"
}
],
"description": "News system - Versatile news system based on Extbase & Fluid and using the latest technologies provided by TYPO3 CMS.",
"homepage": "https://extensions.typo3.org/extension/news",
"keywords": [
"article",
"extension",
"news",
"typo3"
],
"support": {
"docs": "https://docs.typo3.org/p/georgringer/news/main/en-us/",
"issues": "https://github.com/georgringer/news/issues",
"source": "https://github.com/georgringer/news"
},
"funding": [
{
"url": "https://paypal.me/GeorgRinger/10",
"type": "custom"
},
{
"url": "https://www.amazon.de/hz/wishlist/ls/8F573K08TSDG",
"type": "custom"
},
{
"url": "https://github.com/georgringer",
"type": "github"
},
{
"url": "https://www.patreon.com/georgringer",
"type": "patreon"
}
],
"time": "2026-10-12T08:22:46+00:00"
},
{
"name": "gregwar/captcha",
"version": "v2.1.1",
@@ -1676,6 +1785,94 @@
],
"time": "2022-02-21T08:41:21+00:00"
},
{
"name": "lochmueller/staticfilecache",
"version": "15.2.3",
"source": {
"type": "git",
"url": "https://github.com/lochmueller/staticfilecache.git",
"reference": "21697e6b3ea3584995fefe9ab74615927a872744"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lochmueller/staticfilecache/zipball/21697e6b3ea3584995fefe9ab74615927a872744",
"reference": "21697e6b3ea3584995fefe9ab74615927a872744",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"ext-zlib": "*",
"php": "^8.1",
"typo3/cms-backend": "^12.4||^13.4",
"typo3/cms-core": "^12.4||^13.4",
"typo3/cms-extensionmanager": "^12.4||^13.4"
},
"require-dev": {
"andersundsehr/phpstan-git-files": "^1.0",
"friendsofphp/php-cs-fixer": "^3.30",
"phpstan/extension-installer": "^1.3",
"phpstan/phpstan": "^1.10",
"saschaegerer/phpstan-typo3": "^1.8",
"ssch/typo3-rector": "^2.2",
"typo3/testing-framework": "^8.0"
},
"suggest": {
"friendsofsymfony/http-cache": "For HTTP proxy cache services like Varnish",
"lochmueller/seocli": "Scan websites against SEO criteria and/or trigger the indexing process and cache warming in deployment scripts"
},
"type": "typo3-cms-extension",
"extra": {
"typo3/cms": {
"web-dir": ".Build/Web",
"extension-key": "staticfilecache",
"cms-package-dir": "{$vendor-dir}/typo3/cms"
}
},
"autoload": {
"psr-4": {
"SFC\\Staticfilecache\\": "Classes"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Tim Lochmüller",
"email": "[email protected]",
"homepage": "https://github.com/lochmueller",
"role": "Developer"
}
],
"description": "Transparent static file cache solution using mod_rewrite and mod_expires. Increase performance for static pages by a factor of 230!!",
"homepage": "https://extensions.typo3.org/extension/staticfilecache",
"keywords": [
"StaticFileCache",
"TYPO3 CMS",
"cache",
"file",
"magic",
"speed",
"static"
],
"support": {
"docs": "https://docs.typo3.org/p/lochmueller/staticfilecache/main/en-us/",
"issues": "https://github.com/lochmueller/staticfilecache/issues",
"source": "https://github.com/lochmueller/staticfilecache"
},
"funding": [
{
"url": "https://paypal.me/lochmueller",
"type": "custom"
},
{
"url": "https://github.com/lochmueller",
"type": "github"
}
],
"time": "2025-09-02T19:26:27+00:00"
},
{
"name": "lolli42/finediff",
"version": "1.1.2",
@@ -8071,6 +8268,61 @@
},
"time": "2026-10-09T09:26:19+00:00"
},
{
"name": "typo3/cms-scheduler",
"version": "v13.4.36",
"source": {
"type": "git",
"url": "https://github.com/TYPO3-CMS/scheduler.git",
"reference": "5e2d1cddedbf479d17eb5f6768fa006895c194e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/5e2d1cddedbf479d17eb5f6768fa006895c194e9",
"reference": "5e2d1cddedbf479d17eb5f6768fa006895c194e9",
"shasum": ""
},
"require": {
"typo3/cms-core": "13.4.36"
},
"conflict": {
"typo3/cms": "*"
},
"type": "typo3-cms-framework",
"extra": {
"typo3/cms": {
"extension-key": "scheduler"
},
"branch-alias": {
"dev-main": "13.4.x-dev"
}
},
"autoload": {
"psr-4": {
"TYPO3\\CMS\\Scheduler\\": "Classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "TYPO3 Core Team",
"email": "[email protected]",
"role": "Developer"
}
],
"description": "TYPO3 CMS Scheduler - Schedule tasks to run once or periodically at a specific time.",
"homepage": "https://typo3.org",
"support": {
"chat": "https://typo3.org/help",
"docs": "https://docs.typo3.org/c/typo3/cms-scheduler/main/en-us",
"issues": "https://forge.typo3.org",
"source": "https://github.com/typo3/typo3"
},
"time": "2026-10-09T09:26:19+00:00"
},
{
"name": "typo3/cms-seo",
"version": "v13.4.36",
+1
View File
@@ -1,2 +1,3 @@
/secrets.php
/drushrc.php
/settings.php
+30
View File
@@ -64,6 +64,36 @@ if (getenv('IS_DDEV_PROJECT') == 'true') {
);
}
// Redis caches as on TYPO3 10, but in databases 3-5: 0-2 may still hold TYPO3 10 entries. Without a
// connection the backend only logs an alert and every lookup is a miss, so pages still render.
$redisOptions = getenv('IS_DDEV_PROJECT') == 'true'
? ['hostname' => 'redis', 'port' => 6379]
: ['hostname' => '/run/redis_eptypo3/redis.sock', 'port' => 0];
$redisOptions['connectionTimeout'] = 2;
// These replace the definitions from settings.php wholesale, so 'groups' and the compression and
// lifetime options have to be repeated here. Each cache has its own database: RedisBackend::flush()
// issues FLUSHDB.
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages'] = [
'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
'options' => $redisOptions + ['database' => 3, 'compression' => true],
'groups' => ['pages'],
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['hash'] = [
'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
'options' => $redisOptions + ['database' => 4],
'groups' => ['pages'],
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['rootline'] = [
'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
'backend' => \TYPO3\CMS\Core\Cache\Backend\RedisBackend::class,
'options' => $redisOptions + ['database' => 5, 'compression' => true, 'defaultLifetime' => 2592000],
'groups' => ['pages'],
];
unset($redisOptions);
// Deployer creates an empty secrets.php if none exists yet: only an array counts
$secrets = is_file(__DIR__ . '/secrets.php') ? require __DIR__ . '/secrets.php' : null;
if (is_array($secrets)) {
-157
View File
@@ -1,157 +0,0 @@
<?php
return [
'BE' => [
'debug' => false,
'installToolPassword' => '$argon2i$v=19$m=65536,t=16,p=1$NVBiTmJvdEljbWY0WmdjUg$YRkMEpjn+hVLAZWWrrU9wIhVgZ3pPhSnPQyB/7+7Z1g',
'passwordHashing' => [
'className' => 'TYPO3\\CMS\\Core\\Crypto\\PasswordHashing\\Argon2iPasswordHash',
'options' => [],
],
],
'DB' => [
'Connections' => [
'Default' => [
'charset' => 'utf8mb4',
'dbname' => 'db',
'defaultTableOptions' => [
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
'driver' => 'mysqli',
'host' => 'db',
'password' => 'db',
'port' => 3306,
'user' => 'db',
],
],
],
'EXTCONF' => [
'lang' => [
'availableLanguages' => [
'de',
],
],
],
'EXTENSIONS' => [
'backend' => [
'backendFavicon' => '',
'backendLogo' => '',
'loginBackgroundImage' => '',
'loginFootnote' => '',
'loginHighlightColor' => '',
'loginLogo' => '',
'loginLogoAlt' => '',
],
'ep_products' => [
'bpnConnectApiBaseUrl' => '',
'bpnConnectApiKey' => '',
'bpnConnectApiMaxDuration' => '15',
'bpnConnectApiTimeout' => '5',
'cmsApiKey' => '',
'contentFeedEnabled' => '1',
'contentFeedExcludedPages' => '634',
'contentFeedLanguages' => '0',
'contentFeedNewsDetailPages' => 'ep-reisen:1392',
'contentFeedSites' => 'ep-reisen,ser,ski-boarderweek,ski-wochenenden,snowzone',
'importStoragePid' => '11',
'myEpApiBaseUrl' => '',
'myEpApiClientId' => '',
'myEpApiClientSecret' => '',
'myEpApiMaxDuration' => '15',
'myEpApiTimeout' => '5',
'myEpApiUrlAuthorize' => '',
'myEpApiUrlToken' => '',
'myEpStagingUrl' => 'https://my.ep-reisen.net',
'myEpUrl' => 'https://my.ep-reisen.de/',
'searchPageUid' => '0',
],
'ep_theme' => [
'cdnBaseUrl' => 'https://cdn.ep-reisen.de/',
],
'extensionmanager' => [
'automaticInstallation' => '1',
'offlineMode' => '0',
],
'redirects' => [
'showCheckIntegrityInfoInReports' => '1',
'showCheckIntegrityInfoInReportsSeconds' => '86400',
],
],
'FE' => [
'cacheHash' => [
'enforceValidation' => true,
],
'debug' => false,
'disableNoCacheParameter' => true,
'passwordHashing' => [
'className' => 'TYPO3\\CMS\\Core\\Crypto\\PasswordHashing\\Argon2iPasswordHash',
'options' => [],
],
'compressionLevel' => 0,
],
'GFX' => [
'processor' => 'GraphicsMagick',
'processor_effects' => false,
'processor_enabled' => true,
'processor_path' => '/usr/bin/',
],
'LOG' => [
'TYPO3' => [
'CMS' => [
'deprecations' => [
'writerConfiguration' => [
'notice' => [
'TYPO3\CMS\Core\Log\Writer\FileWriter' => [
'disabled' => true,
],
],
],
],
],
],
],
'MAIL' => [
'transport' => 'sendmail',
'transport_sendmail_command' => '/usr/sbin/sendmail -t -i',
'transport_smtp_encrypt' => '',
'transport_smtp_password' => '',
'transport_smtp_server' => '',
'transport_smtp_username' => '',
],
'SYS' => [
'UTF8filesystem' => true,
'caching' => [
'cacheConfigurations' => [
'hash' => [
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
],
'pages' => [
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
'options' => [
'compression' => true,
],
],
'rootline' => [
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
'options' => [
'compression' => true,
],
],
],
],
'devIPmask' => '',
'displayErrors' => 0,
'encryptionKey' => '0ad297595e43cb5f668bdf7878eaea7002a4193fd26d2742a3a708b4b0715bcfabdae84a47ca8517d55732d9ea72a26a',
'exceptionalErrors' => 4096,
'features' => [
'extbase.consistentDateTimeHandling' => true,
'frontend.cache.autoTagging' => true,
'security.system.enforceAllowedFileExtensions' => true,
],
'sitename' => 'E&P',
'systemMaintainers' => [
1,
],
'trustedHostsPattern' => '(?:[a-z0-9-]+\\.)?(?:ep-reisen\\.de|ski-boarderweek\\.(?:de|com|eu)|skiboarderweek\\.(?:de|eu)|snowzone\\.net|unichamp\\.de|snow-break\\.(?:de|com)|semesterende-skireisen\\.de|alpinevacation\\.ch|cllt-touristik\\.at|ep-events\\.de|ski-wochenenden\\.de)',
],
];
+31 -5
View File
@@ -36,8 +36,13 @@ set('shared_dirs', [
// Baseline of the MyE&P import: without it every deploy would trigger a full re-import
'var/transient/ep_products_import',
]);
// settings.php is shared as LocalConfiguration.php was on TYPO3 10: production is leading, so what
// is changed in the backend (extension configuration, install tool) survives deploys and rollbacks.
// It is not in git (encryptionKey, install tool password): configuration changes for production are
// made there, in the backend or by hand on the shared file.
set('shared_files', [
'config/system/secrets.php',
'config/system/settings.php',
]);
set('writable_dirs', [
'{{typo3/public_dir}}/typo3temp/assets',
@@ -138,6 +143,21 @@ task('typo3:secrets:init', function () {
writeln('<comment>Created ' . parse($target) . ' from the template: fill it in, then deploy again.</comment>');
});
desc('Seed the shared settings.php from the live release unless it is there already (never overwrites)');
task('typo3:settings:init', function () {
$target = '{{deploy_path}}/shared/config/system/settings.php';
if (test("[ -s $target ]")) {
return;
}
// deploy:shared would seed it from the new release, i.e. from git, and drop production's settings
if (!test('[ -f {{deploy_path}}/current/config/system/settings.php ]')) {
return;
}
run('mkdir -p {{deploy_path}}/shared/config/system');
run("cp -L {{deploy_path}}/current/config/system/settings.php $target");
writeln('<comment>Created ' . parse($target) . ' from the live release.</comment>');
});
desc('Stop before the database is touched while secrets.php still lacks the database connection');
task('typo3:secrets:check', function () {
$secrets = '{{deploy_path}}/shared/config/system/secrets.php';
@@ -154,13 +174,15 @@ task('typo3:extension:setup', function () {
desc('TYPO3 - Clear all caches');
task('typo3:cache:flush', function () {
run('{{bin/php}} {{release_path}}/{{bin/typo3}} cache:flush');
run('{{bin/php}} {{current_path}}/{{bin/typo3}} cache:flush');
});
desc('Link the production fileadmin into the release');
task('typo3:fileadmin', function () {
run('test -d {{fileadmin_path}}');
run('ln -sfn {{fileadmin_path}} {{release_path}}/{{typo3/public_dir}}/fileadmin');
// Relative (-r), like Deployer's shared dirs: the vhosts answer 403 for an absolute symlink here
run('ln -sfnr {{fileadmin_path}} {{release_path}}/{{typo3/public_dir}}/fileadmin');
run('test -d {{release_path}}/{{typo3/public_dir}}/fileadmin/user_upload');
});
desc('Deploys TYPO3 13');
@@ -173,6 +195,7 @@ task('deploy', [
'deploy:release',
'rsync',
'typo3:secrets:init',
'typo3:settings:init',
'deploy:shared',
'typo3:fileadmin',
'deploy:writable',
@@ -186,9 +209,12 @@ task('deploy', [
// Caches belong to the release that goes live, so they are cleared after the symlink flip. Hooking
// into deploy:symlink rather than appending to the list above keeps them inside deploy:publish, ahead
// of deploy:unlock and deploy:success - a failing cache task must not follow a declared success.
// No OPcache reset (cachetool): its web adapter would need PHP under www.ep-reisen.de/typo3temp, which
// is the static docroot. Not needed either: the front controller stubs in the static docroots resolve
// "current" with readlink() on every request, so a new release is always loaded under new paths.
// No OPcache reset (cachetool): its web adapter would need PHP under a host's typo3temp. Vhosts that
// point at current/public keep running the previous release until PHP's path cache expires (~2 min);
// pages it renders in that window stay in the page cache until the next flush.
after('deploy:symlink', 'typo3:cache:flush');
// A rollback switches the code back; pages cached by the other release must not survive it
after('rollback', 'typo3:cache:flush');
after('deploy:failed', 'deploy:unlock');
@@ -0,0 +1,82 @@
<?php
namespace EP\EpProducts\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository;
use TYPO3\CMS\Scheduler\Task\ExecuteSchedulableCommandTask;
#[AsCommand(name: 'ep:scheduler-tasks', description: 'Replaces the TYPO3 10 import tasks with scheduler tasks running the ep:* commands')]
class SchedulerTasksCommand extends Command
{
/**
* TYPO3 10 tasks TYPO3 13 cannot run: 5/10 ImportSnowreportTask/ImportDatesTask (classes gone), 6 ImportContingentsTask
* (off since 2026-08), 16-21 one ep:groups-swiss task per house (API keys in the database), 25 garbage collection of
* the retired tx_epevents_log. The core housekeeping tasks 13, 14, 22, 23 and 24 work unchanged.
*/
private const OLD_TASKS = [5, 6, 10, 16, 17, 18, 19, 20, 21, 25];
/**
* Same schedules and groups as on TYPO3 10 (1 Datenimport, 2 Datenexport); ep:groups-swiss without arguments
* exports all houses of groupsSwissHouses in secrets.php.
*/
private const NEW_TASKS = [
['command' => 'ep:import-dates', 'cron' => '*/15 6-20 * * *', 'multiple' => true, 'group' => 1, 'description' => 'Reisedaten aus MyE&P importieren'],
['command' => 'ep:import-snowreport', 'cron' => '0 6 * * *', 'multiple' => false, 'group' => 1, 'description' => 'Schneehöhen importieren'],
['command' => 'ep:groups-swiss', 'cron' => '0 14 * * *', 'multiple' => false, 'group' => 2, 'description' => 'Belegung der Gruppenhäuser nach groups.swiss exportieren'],
];
public function __construct(
private readonly SchedulerTaskRepository $taskRepository,
private readonly ConnectionPool $connectionPool,
) {
parent::__construct();
}
protected function configure(): void
{
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Only show what would change');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$dryRun = (bool)$input->getOption('dry-run');
$existing = [];
foreach ($this->connectionPool->getConnectionForTable('tx_scheduler_task')->select(['uid', 'serialized_task_object'], 'tx_scheduler_task', ['deleted' => 0])->fetchAllAssociative() as $row) {
if (in_array((int)$row['uid'], self::OLD_TASKS, true)) {
$output->writeln(sprintf('Delete TYPO3 10 task %d', $row['uid']));
if (!$dryRun) {
$this->taskRepository->remove((int)$row['uid']);
}
} elseif (preg_match('/s:20:"\0\*\0commandIdentifier";s:\d+:"([^"]+)"/', (string)$row['serialized_task_object'], $matches)) {
$existing[$matches[1]] = (int)$row['uid'];
}
}
foreach (self::NEW_TASKS as $config) {
if (isset($existing[$config['command']])) {
$output->writeln(sprintf('Task for %s exists (%d)', $config['command'], $existing[$config['command']]));
continue;
}
$output->writeln(sprintf('Add task for %s (%s)', $config['command'], $config['cron']));
if ($dryRun) {
continue;
}
$task = GeneralUtility::makeInstance(ExecuteSchedulableCommandTask::class);
$task->setCommandIdentifier($config['command']);
$task->setDescription($config['description']);
$task->setTaskGroup($config['group']);
$task->registerRecurringExecution($GLOBALS['EXEC_TIME'] - 1, 0, 0, $config['multiple'], $config['cron']);
$this->taskRepository->add($task);
}
return Command::SUCCESS;
}
}
@@ -0,0 +1,99 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\City;
use EP\EpProducts\Domain\Repository\CityRepository;
use EP\EpProducts\Service\ProductDataService;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class CityController extends ActionController
{
/**
* @var CityRepository
*/
protected $cityRepository;
/**
* @var ProductDataService
*/
private $productDataService;
/**
* @param CityRepository $cityRepository
* @param ProductDataService $productDataService
*/
public function __construct(CityRepository $cityRepository, ProductDataService $productDataService)
{
$this->cityRepository = $cityRepository;
$this->productDataService = $productDataService;
}
/**
* @param City $city
*/
public function detailAction(?City $city = null): ResponseInterface
{
if (null === $city) {
$cityUid = ($this->settings['cityUid'] ?? '');
$city = $this->cityRepository->findByIdentifier($cityUid);
}
$teasers = [];
if (null !== $city) {
$filterSettings = [
'cityUids' => [$cityUid]
];
$teasers = $this->productDataService->getTeasergroup($filterSettings);
}
$this->view->assignMultiple([
'city' => $city,
'teasers' => $teasers,
'filterSettings' => $filterSettings,
]);
return $this->htmlResponse();
}
public function teasergroupAction(): ResponseInterface
{
$regionUid = (int)($this->settings['regionUid'] ?? '');
$countryUid = (int)($this->settings['countryUid'] ?? '');
$season = ($this->settings['season'] ?? '');
$citiesPid = (int)($this->settings['citiesStoragePid'] ?? '');
$cities = $this->cityRepository->getTeasersByCountryOrRegionAndSeason($regionUid, $countryUid, $season, $citiesPid);
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assign('data', $data);
$this->view->assign('teasers', $cities);
return $this->htmlResponse();
}
}
@@ -0,0 +1,106 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Concept;
use EP\EpProducts\Domain\Repository\ConceptRepository;
use EP\EpProducts\Service\ProductDataService;
use EP\EpTheme\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ConceptController extends ActionController
{
/**
* @var ConceptRepository
*/
protected $conceptRepository;
/**
* @var ProductDataService
*/
protected $productDataService;
/**
* @var PageRepository
*/
private $pageRepository;
/**
* @param ConceptRepository $conceptRepository
* @param ProductDataService $productDataService
* @param PageRepository $pageRepository
*/
public function __construct(ConceptRepository $conceptRepository, ProductDataService $productDataService, PageRepository $pageRepository)
{
$this->conceptRepository = $conceptRepository;
$this->productDataService = $productDataService;
$this->pageRepository = $pageRepository;
}
/**
* @param Concept $concept
*/
public function detailAction(?Concept $concept = null): ResponseInterface
{
if ($concept === null) {
$conceptUid = ($this->settings['conceptUid'] ?? '');
$concept = $this->conceptRepository->findByIdentifier($conceptUid);
}
$teaserConceptUids = GeneralUtility::trimExplode(',', ($this->settings['additionalConceptUids'] ?? ''));
if (null !== $concept) {
array_unshift($teaserConceptUids, $concept->getUid());
}
$filterSettings = ['conceptUids' => $teaserConceptUids];
$excludedConceptUids = GeneralUtility::trimExplode(',', ($this->settings['excludedConceptUids'] ?? ''), true);
$limit = ($this->settings['showMoreLink'] ?? '') ? 6 : 0;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
$pages = $this->pageRepository->findPagesByPid((int)($this->settings['conceptsPageUid'] ?? ''));
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assignMultiple([
'teasers' => $teasers,
'concept' => $concept,
'filterSettings' => $filterSettings,
'pages' => $pages,
'data' => $data,
]);
return $this->htmlResponse();
}
public function teasergroupAction(): ResponseInterface
{
$concepts = $this->conceptRepository->findAll();
$this->view->assign('teasers', $concepts);
return $this->htmlResponse();
}
}
@@ -0,0 +1,80 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Country;
use EP\EpProducts\Domain\Repository\CountryRepository;
use EP\EpProducts\Service\ProductDataService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class CountryController extends ActionController
{
/**
* @var CountryRepository
*/
protected $countryRepository;
/**
* @var ProductDataService
*/
protected $productDataService;
/**
* @param CountryRepository $countryRepository
* @param ProductDataService $productDataService
*/
public function __construct(CountryRepository $countryRepository, ProductDataService $productDataService)
{
$this->countryRepository = $countryRepository;
$this->productDataService = $productDataService;
}
/**
* @param Country $country
*/
public function detailAction(?Country $country = null): ResponseInterface
{
if ($country === null) {
$countryUid = ($this->settings['countryUid'] ?? '');
$country = $this->countryRepository->findByIdentifier($countryUid);
}
$filterSettings = [
'countryUids' => [ $countryUid ],
];
$excludedConceptUids = GeneralUtility::trimExplode(',', ($this->settings['excludedConceptUids'] ?? ''), true);
$limit = ($this->settings['showMoreLink'] ?? '') ? 6 : 0;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
$this->view->assign('teasers', $teasers);
$this->view->assign('country', $country);
$this->view->assign('filterSettings', $filterSettings);
return $this->htmlResponse();
}
}
@@ -0,0 +1,57 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Repository\FaqRepository;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class FaqController extends ActionController
{
/**
* @var FaqRepository
*/
protected $faqRepository;
/**
* @param FaqRepository $faqRepository
*/
public function __construct(FaqRepository $faqRepository)
{
$this->faqRepository = $faqRepository;
}
public function detailAction(): ResponseInterface
{
$faqUids = ($this->settings['faqUids'] ?? '');
$faqs = $this->faqRepository->findByUids($faqUids);
$this->view->assign('faqs', $faqs);
return $this->htmlResponse();
}
}
@@ -0,0 +1,157 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Product;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\HotelRepository;
use EP\EpProducts\Domain\Repository\ProductRepository;
use EP\EpProducts\Service\HotelDataService;
use EP\EpProducts\Service\ProductDataService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class HotelController extends ActionController
{
/**
* @var HotelRepository
*/
protected $hotelRepository;
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var HotelDataService
*/
protected $hotelDataService;
/**
* @var ProductDataService
*/
protected $productDataService;
/**
* @param HotelDataService $hotelDataService
* @param HotelRepository $hotelRepository
* @param ProductRepository $productRepository
* @param ProductDataService $productDataService
*/
public function __construct
(
HotelDataService $hotelDataService,
HotelRepository $hotelRepository,
ProductRepository $productRepository,
ProductDataService $productDataService
)
{
$this->hotelRepository = $hotelRepository;
$this->productRepository = $productRepository;
$this->hotelDataService = $hotelDataService;
$this->productDataService = $productDataService;
}
/**
* @param Hotel $hotel
*/
public function detailAction(?Hotel $hotel = null): ResponseInterface
{
if ($hotel !== null) {
$hotelUid = $hotel->getUid();
} else {
$hotelUid = ($this->settings['hotelUid'] ?? '');
/** @var Hotel $hotel */
$hotel = $this->hotelRepository->findByIdentifier($hotelUid);
}
$filterSettings = [
'hotelUid' => $hotelUid,
];
$excludedConceptUids = GeneralUtility::trimExplode(',', ($this->settings['excludedConceptUids'] ?? ''), true);
$limit = ($this->settings['hideMoreLink'] ?? '') ? 0 : 6;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
$this->view->assign('hotel', $hotel);
$this->view->assign('teasers', $teasers);
$this->view->assign('filterSettings', $filterSettings);
return $this->htmlResponse();
}
public function detailEventAction(): ResponseInterface
{
$productUid = ($this->settings['productUid'] ?? '');
/** @var Product $product */
$product = $this->productRepository->findByIdentifier($productUid);
$hotelUid = ($this->settings['hotelUid'] ?? '');
/** @var Hotel $hotel */
$hotel = $this->hotelRepository->findByIdentifier($hotelUid);
$this->view->assignMultiple([
'product' => $product,
'hotel' => $hotel,
]);
return $this->htmlResponse();
}
public function teasergroupAction(): ResponseInterface
{
$criteria = [
'regionUid' => (int) ($this->settings['regionUid'] ?? ''),
'countryUid' => (int) ($this->settings['countryUid'] ?? ''),
'conceptUid' => (int) ($this->settings['conceptUid'] ?? ''),
'productUid' => (int) ($this->settings['productUid'] ?? ''),
'category' => (int) ($this->settings['category'] ?? ''),
'type' => (int) ($this->settings['type'] ?? ''),
];
$bookable = $this->hotelDataService->getTeasersByCriteria($criteria);
$bookableHotelUids = array_keys($bookable);
$nonBookable = $this->hotelDataService->getDateIndependentTeasersByCriteria($criteria, $bookableHotelUids);
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assign('data', $data);
$this->view->assign('bookable', $bookable);
$this->view->assign('nonBookable', $nonBookable);
return $this->htmlResponse();
}
public function dateIndependentTeasergroupAction(): ResponseInterface
{
$criteria = [
'regionUid' => (int) ($this->settings['regionUid'] ?? ''),
'countryUid' => (int) ($this->settings['countryUid'] ?? ''),
'productUid' => (int) ($this->settings['productUid'] ?? ''),
'category' => (int) ($this->settings['category'] ?? ''),
'type' => (int) ($this->settings['type'] ?? ''),
];
$hotels = $this->hotelDataService->getDateIndependentTeasersByCriteria($criteria);
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assign('data', $data);
$this->view->assign('teasers', $hotels);
return $this->htmlResponse();
}
}
@@ -0,0 +1,126 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Model\Product;
use EP\EpProducts\Domain\Repository\HotelRepository;
use EP\EpProducts\Domain\Repository\ProductRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
class MapController extends ActionController
{
/**
* @var HotelRepository
*/
protected $hotelRepository;
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @param HotelRepository $hotelRepository
* @param ProductRepository $productRepository
*/
public function __construct(
HotelRepository $hotelRepository,
ProductRepository $productRepository
)
{
$this->hotelRepository = $hotelRepository;
$this->productRepository = $productRepository;
}
public function generalAction(): ResponseInterface
{
$markerData = [];
$selectedHotels = ($this->settings['hotelUids'] ?? '');
$hotelUids = GeneralUtility::trimExplode(',', $selectedHotels);
$hotels = $this->hotelRepository->getListByUids($hotelUids);
if ($hotels) {
foreach ($hotels as $hotel)
{
/** @var Hotel $hotel */
$markerData[] = [
'name' => $hotel->getName(),
'uri' => $this->getUriForPageUid($hotel->getDetailPage()),
'teaser' => $hotel->getTeaser(),
'lat' => $hotel->getLatitude(),
'lng' => $hotel->getLongitude(),
];
}
}
$selectedProducts = ($this->settings['productUids'] ?? '');
$productUids = GeneralUtility::trimExplode(',', $selectedProducts);
$products = $this->productRepository->getListByUids($productUids);
if ($products) {
foreach ($products as $product)
{
/** @var Product $product */
$hotel = $product->getHotels()->current();
if (null === $hotel || false === $hotel->isShowAsTeaser()) {
continue;
}
$markerData[] = [
'name' => $product->getName(),
'uri' => $this->getUriForPageUid($product->getDetailPage()),
'externalLink' => $hotel->getExternalLink(),
'teaser' => preg_replace("/[\r\n\t]/", "", $product->getTeaser()),
'lat' => $hotel->getLatitude(),
'lng' => $hotel->getLongitude(),
];
}
}
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assign('data', $data);
$this->view->assign('markerData', json_encode($markerData, JSON_THROW_ON_ERROR | JSON_HEX_APOS));
return $this->htmlResponse();
}
/**
* @param int $pageUid
* @return string
*/
protected function getUriForPageUid($pageUid)
{
/** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
return $contentObject->typoLink_URL(
[
'parameter' => $pageUid,
]
);
}
}
@@ -0,0 +1,42 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class PricetableController extends ActionController
{
public function eventAction(): ResponseInterface
{
$productUid = ($this->settings['productUid'] ?? '');
$this->view->assign('productUid', $productUid);
return $this->htmlResponse();
}
}
@@ -0,0 +1,97 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Region;
use EP\EpProducts\Domain\Repository\RegionRepository;
use EP\EpProducts\Service\ProductDataService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class RegionController extends ActionController
{
/**
* @var RegionRepository
*/
protected $regionRepository;
/**
* @var ProductDataService
*/
protected $productDataService;
/**
* @param RegionRepository $regionRepository
* @param ProductDataService $productDataService
*/
public function __construct(RegionRepository $regionRepository, ProductDataService $productDataService)
{
$this->regionRepository = $regionRepository;
$this->productDataService = $productDataService;
}
/**
* @param Region $region
*/
public function detailAction(?Region $region = null): ResponseInterface
{
if ($region === null) {
$regionUid = ($this->settings['regionUid'] ?? '');
$region = $this->regionRepository->findByIdentifier($regionUid);
}
$additionalRegionUids = GeneralUtility::trimExplode(',', ($this->settings['additionalRegionUids'] ?? ''), true);
$additionalRegionUids = array_map(function ($uid) {
return (int)$uid;
}, $additionalRegionUids);
$teaserRegionUids = array_merge([$region->getUid()], $additionalRegionUids);
$filterSettings = [
'regionUids' => $teaserRegionUids,
];
$excludedConceptUids = GeneralUtility::trimExplode(',', ($this->settings['excludedConceptUids'] ?? ''), true);
$limit = ($this->settings['hideMoreLink'] ?? '') ? 0 : 6;
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
$this->view->assign('teasers', $teasers);
$this->view->assign('region', $region);
$this->view->assign('filterSettings', $filterSettings);
return $this->htmlResponse();
}
public function teasergroupAction(): ResponseInterface
{
$countryUid = (int)($this->settings['countryUid'] ?? '');
$season = ($this->settings['season'] ?? '');
$regionsPid = (int)($this->settings['regionsStoragePid'] ?? '');
$regions = $this->regionRepository->getTeasersByCountryAndSeason($countryUid, $season, $regionsPid);
$data = $this->request->getAttribute('currentContentObject')->data;
$this->view->assign('data', $data);
$this->view->assign('teasers', $regions);
return $this->htmlResponse();
}
}
@@ -0,0 +1,98 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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 Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Teammember;
use EP\EpProducts\Domain\Repository\TeammemberRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class TeamController extends ActionController
{
/**
* @var TeammemberRepository
*/
protected $teammemberRepository;
/**
* @param TeammemberRepository $teammemberRepository
*/
public function __construct(TeammemberRepository $teammemberRepository)
{
$this->teammemberRepository = $teammemberRepository;
}
public function listAction(): ResponseInterface
{
if (isset($this->settings['teamCategories']) && count(GeneralUtility::trimExplode(',', ($this->settings['teamCategories'] ?? ''), true)) > 0) {
$members = $this->teammemberRepository->findByCategories(GeneralUtility::trimExplode(',', ($this->settings['teamCategories'] ?? ''), true));
} else {
$members = $this->teammemberRepository->findAll();
}
$this->view->assign('members', $members);
return $this->htmlResponse();
}
/**
* @param Teammember $member
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
*/
public function showAction(?Teammember $member = null): ResponseInterface
{
if ($member === null) {
return $this->redirect('list', null, null, null, null, 0, 302);
}
$this->view->assign('member', $member);
$this->view->assign('data', $this->request->getAttribute('currentContentObject')->data);
$this->view->assign('header', $this->coalesceHeader($member));
return $this->htmlResponse();
}
/**
* @param Teammember $member
*
* @return string
*/
public function coalesceHeader(Teammember $member)
{
$data = $this->request->getAttribute('currentContentObject')->data;
if (isset($data['header']) && $data['header'] !== '') {
return $data['header'];
}
$firstCategory = current($member->getCategories()->toArray());
if ($firstCategory !== null && $firstCategory !== false) {
return $firstCategory->getTitle();
}
return '';
}
}
@@ -96,7 +96,7 @@ abstract class AbstractRepository extends Repository
protected function addHotelFilterConditions(QueryBuilder $qb, array $filterSettings): void
{
// Hotel
if ($filterSettings['hotelUid'] > 0) {
if (($filterSettings['hotelUid'] ?? 0) > 0) {
$qb->andWhere($qb->expr()->eq(
'date.hotel',
$qb->createNamedParameter((int)$filterSettings['hotelUid'], Connection::PARAM_INT)
@@ -0,0 +1,63 @@
<?php
namespace EP\EpProducts\Domain\Repository;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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\Domain\Model\Faq;
use Doctrine\DBAL\ArrayParameterType;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
class FaqRepository extends AbstractRepository
{
/**
* @param string $uids
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function findByUids($uids)
{
/** @var DataMapper $dataMapper */
$dataMapper = GeneralUtility::makeInstance(DataMapper::class);
$uids = GeneralUtility::intExplode(',', (string)$uids, true);
if ($uids === []) {
return [];
}
$qb = $this->getDbConnection()->createQueryBuilder();
$qb
->select('*')
->from('tx_epproducts_domain_model_faq')
->where('uid IN (:uids)')
->setParameter('uids', $uids, ArrayParameterType::INTEGER);
$qb->getConcreteQueryBuilder()->orderBy('FIELD(uid, ' . implode(',', $uids) . ')');
$rows = $qb->executeQuery()->fetchAllAssociative();
return $dataMapper->map(Faq::class, $rows);
}
}
@@ -0,0 +1,54 @@
<?php
namespace EP\EpProducts\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
/***************************************************************
*
* Copyright notice
*
* (c) 2016 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!
***************************************************************/
class TeammemberRepository extends AbstractRepository
{
/**
* @var array
*/
protected $defaultOrderings = [
'sorting' => 'ASC',
];
/**
* @param array $categories
* @return array|QueryResultInterface
* @throws InvalidQueryException
*/
public function findByCategories(array $categories = [])
{
$qb = $this->createQuery();
$qb->matching($qb->in('categories.uid', $categories));
return $qb->execute();
}
}
@@ -79,8 +79,7 @@ class HotelDataService implements SingletonInterface
$hotelData['available'] = -1; // TODO: Needs improvement. '-1' actually means: no price/date info available
}
$hotelCategory = array_key_exists($hotelData['hotelCategory'], Hotel::$categoryLabels) ?
Hotel::$categoryLabels[$hotelData['hotelCategory']] : null;
$hotelCategory = Hotel::$categoryLabels[$hotelData['hotelCategory'] ?? ''] ?? null;
$dateStart = new \DateTime($hotelData['dateStart'] ?? 'now');
$dateEnd = new \DateTime($hotelData['dateEnd'] ?? 'now');
@@ -147,7 +146,7 @@ class HotelDataService implements SingletonInterface
'minDateStart' => null,
'maxDateEnd' => null,
'minPrice' => null,
'pseudoPrice' => $row['pseudoPrice'],
'pseudoPrice' => $row['pseudoPrice'] ?? null,
'available' => 0,
'detailPage' => $row['hotelDetailPage'],
'name' => $row['hotelName'],
@@ -159,16 +158,18 @@ class HotelDataService implements SingletonInterface
'code' => $row['countryCode'],
],
'region' => [
'name' => $row['regionName'],
'name' => $row['regionName'] ?? null,
]
];
}
$teaser =& $teasers[$hotelUid];
$teaser['available'] += $row['available'];
// Hotels without dates (date-independent teasers) have no date columns: as PHP 7.4 read
// them, they count as not available, with today as date and no price
$teaser['available'] += $row['available'] ?? 0;
$dateStart = new \DateTime($row['dateStart']);
$dateEnd = new \DateTime($row['dateEnd']);
$dateStart = new \DateTime($row['dateStart'] ?? 'now');
$dateEnd = new \DateTime($row['dateEnd'] ?? 'now');
if ($teaser['minDateStart'] === null || $teaser['minDateStart'] > $dateStart) {
$teaser['minDateStart'] = $dateStart;
@@ -178,8 +179,8 @@ class HotelDataService implements SingletonInterface
$teaser['maxDateEnd'] = $dateEnd;
}
if ($teaser['minPrice'] === null || $teaser['minPrice'] > $row['minPrice']) {
$teaser['minPrice'] = $row['minPrice'];
if ($teaser['minPrice'] === null || $teaser['minPrice'] > ($row['minPrice'] ?? null)) {
$teaser['minPrice'] = $row['minPrice'] ?? null;
}
}
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.cityUid>
<exclude>0</exclude>
<label>Ort</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_city</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_city.hidden = 0 AND tx_epproducts_domain_model_city.deleted = 0 ORDER BY tx_epproducts_domain_model_city.name</foreign_table_where>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.cityUid>
<settings.hideMoreLink>
<exclude>0</exclude>
<label>Link zum Suchergebnis ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideMoreLink>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,72 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.countryUid>
<exclude>0</exclude>
<label>Land</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_country</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0"/>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.countryUid>
<settings.regionUid>
<exclude>0</exclude>
<label>Gebiet</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_region</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0"/>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.regionUid>
<settings.season>
<exclude>0</exclude>
<label>Saison:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Winter</numIndex>
<numIndex index="1">w</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Sommer</numIndex>
<numIndex index="1">s</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.season>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,60 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.conceptUid>
<exclude>0</exclude>
<label>Konzept:</label>
<config>
<type>select</type>
<renderType>selectSingle</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>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Alle</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.conceptUid>
<settings.additionalConceptUids>
<label>Weitere Konzepte f&#xFC;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>
</settings.additionalConceptUids>
<settings.hideMoreLink>
<exclude>0</exclude>
<label>Link zum Suchergebnis ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideMoreLink>
<settings.hideHeader>
<exclude>0</exclude>
<label>Header ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideHeader>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,41 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.countryUid>
<exclude>0</exclude>
<label>Land</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_country</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_country.hidden = 0 AND tx_epproducts_domain_model_country.deleted = 0 ORDER BY tx_epproducts_domain_model_country.name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0"/>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.countryUid>
<settings.hideMoreLink>
<exclude>0</exclude>
<label>Link zum Suchergebnis ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideMoreLink>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.faqUids>
<exclude>0</exclude>
<label>Fragen/Antworten</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<foreign_table>tx_epproducts_domain_model_faq</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_faq.hidden = 0 AND tx_epproducts_domain_model_faq.deleted = 0 ORDER BY tx_epproducts_domain_model_faq.label</foreign_table_where>
<minitems>1</minitems>
<maxitems>50</maxitems>
<size>10</size>
</config>
</settings.faqUids>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,41 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.hotelUid>
<exclude>0</exclude>
<label>Hotel:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_hotel</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_hotel.hidden = 0 AND tx_epproducts_domain_model_hotel.deleted = 0 ORDER BY tx_epproducts_domain_model_hotel.name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Alle</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.hotelUid>
<settings.hideMoreLink>
<exclude>0</exclude>
<label>Link zum Suchergebnis ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideMoreLink>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.productUid>
<exclude>0</exclude>
<label>Produkt:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_product</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_product.hidden = 0 AND tx_epproducts_domain_model_product.deleted = 0 ORDER BY tx_epproducts_domain_model_product.name_internal</foreign_table_where>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.productUid>
<settings.hotelUid>
<exclude>0</exclude>
<label>Hotel:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_hotel</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_hotel.hidden = 0 AND tx_epproducts_domain_model_hotel.deleted = 0 ORDER BY tx_epproducts_domain_model_hotel.name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Alle</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.hotelUid>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,200 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.columns>
<exclude>0</exclude>
<label>Spalten:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">vierspaltig</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">dreispaltig</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">zweispaltig</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.columns>
<settings.countryUid>
<exclude>0</exclude>
<label>Land:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_country</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.countryUid>
<settings.regionUid>
<exclude>0</exclude>
<label>Gebiet:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_region</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.regionUid>
<settings.cityUid>
<exclude>0</exclude>
<label>Ort:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_city</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.cityUid>
<settings.conceptUid>
<label>Konzept:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_concept</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.conceptUid>
<settings.productUid>
<label>Produkt:</label>
<onChange>reload</onChange>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_product</foreign_table>
<foreign_table_where>ORDER BY name_internal</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.productUid>
<settings.category>
<label>Kategorie:</label>
<displayCond>FIELD:settings.productUid:REQ:TRUE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Alle</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Standard</numIndex>
<numIndex index="1">1</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">Basic</numIndex>
<numIndex index="1">5</numIndex>
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">Komfort</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
<numIndex index="4" type="array">
<numIndex index="0">Deluxe</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="5" type="array">
<numIndex index="0">Superdeluxe</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.category>
<settings.type>
<label>Unterkunftsart:</label>
<config>
<type>select</type>
<renderType>selectCheckBox</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Ferienwohnung</numIndex>
<numIndex index="1">1</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Sportclub</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">Apartment</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">G&#xE4;sterhaus</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
<numIndex index="4" type="array">
<numIndex index="0">Pension</numIndex>
<numIndex index="1">5</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>10</maxitems>
</config>
</settings.type>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,163 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.columns>
<exclude>0</exclude>
<label>Spalten:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">vierspaltig</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">dreispaltig</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">zweispaltig</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.columns>
<settings.countryUid>
<exclude>0</exclude>
<label>Land:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_country</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.countryUid>
<settings.regionUid>
<exclude>0</exclude>
<label>Gebiet:</label>
<displayCond>FIELD:settings.productUid:REQ:FALSE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_region</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.regionUid>
<settings.productUid>
<label>Produkt:</label>
<onChange>reload</onChange>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_product</foreign_table>
<foreign_table_where>ORDER BY name_internal</foreign_table_where>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Keine Zuordnung</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.productUid>
<settings.category>
<label>Kategorie:</label>
<displayCond>FIELD:settings.productUid:REQ:TRUE</displayCond>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Alle</numIndex>
<numIndex index="1">0</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Standard</numIndex>
<numIndex index="1">1</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">Basic</numIndex>
<numIndex index="1">5</numIndex>
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">Komfort</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
<numIndex index="4" type="array">
<numIndex index="0">Deluxe</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="5" type="array">
<numIndex index="0">Superdeluxe</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
</config>
</settings.category>
<settings.type>
<label>Unterkunftsart:</label>
<config>
<type>select</type>
<renderType>selectCheckBox</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Ferienwohnung</numIndex>
<numIndex index="1">1</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Sportclub</numIndex>
<numIndex index="1">2</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">Apartment</numIndex>
<numIndex index="1">3</numIndex>
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">G&#xE4;sterhaus</numIndex>
<numIndex index="1">4</numIndex>
</numIndex>
<numIndex index="4" type="array">
<numIndex index="0">Pension</numIndex>
<numIndex index="1">5</numIndex>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>10</maxitems>
</config>
</settings.type>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,42 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.hotelUids>
<exclude>0</exclude>
<label>Unterk&#xFC;nfte</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<foreign_table>tx_epproducts_domain_model_hotel</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_hotel.latitude != "" AND tx_epproducts_domain_model_hotel.longitude != "" ORDER BY name</foreign_table_where>
<minitems>0</minitems>
<maxitems>50</maxitems>
<size>10</size>
</config>
</settings.hotelUids>
<settings.productUids>
<exclude>0</exclude>
<label>Produkte</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<foreign_table>tx_epproducts_domain_model_product</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_product.detail_page != 0 ORDER BY name_internal</foreign_table_where>
<minitems>0</minitems>
<maxitems>50</maxitems>
<size>10</size>
</config>
</settings.productUids>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<switchableControllerActions>
<label>Modus</label>
<onChange>reload</onChange>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<default>Pricetable->event</default>
<items type="array">
<numIndex index="1" type="array">
<numIndex index="0">Preistabelle Eventreise</numIndex>
<numIndex index="1">Pricetable->event</numIndex>
</numIndex>
</items>
</config>
</switchableControllerActions>
<settings.forcePartnerIdSelect>
<label>Partner ID Auswahl anzeigen:</label>
<config>
<type>check</type>
<default>0</default>
</config>
</settings.forcePartnerIdSelect>
<settings.partnerIdSelectLabel>
<label>Partner ID Auswahl Text:</label>
<config>
<type>input</type>
<eval>trim</eval>
</config>
</settings.partnerIdSelectLabel>
<settings.productUid>
<label>Produkt:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_product</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_product.hidden = 0 AND tx_epproducts_domain_model_product.deleted = 0 ORDER BY tx_epproducts_domain_model_product.name_internal</foreign_table_where>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.productUid>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,47 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.regionUid>
<exclude>0</exclude>
<label>Gebiet:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_region</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_region.hidden = 0 AND tx_epproducts_domain_model_region.deleted = 0 ORDER BY tx_epproducts_domain_model_region.name</foreign_table_where>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.regionUid>
<settings.additionalRegionUids>
<exclude>0</exclude>
<label>Weitere Gebiete f&#xFC;r Teaser:</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<foreign_table>tx_epproducts_domain_model_region</foreign_table>
<foreign_table_where>AND tx_epproducts_domain_model_region.hidden = 0 AND tx_epproducts_domain_model_region.deleted = 0 ORDER BY tx_epproducts_domain_model_region.name</foreign_table_where>
<minitems>0</minitems>
<maxitems>5</maxitems>
</config>
</settings.additionalRegionUids>
<settings.hideMoreLink>
<exclude>0</exclude>
<label>Link zum Suchergebnis ausblenden:</label>
<config>
<type>check</type>
</config>
</settings.hideMoreLink>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,48 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.countryUid>
<exclude>0</exclude>
<label>Land:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<foreign_table>tx_epproducts_domain_model_country</foreign_table>
<foreign_table_where>ORDER BY name</foreign_table_where>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.countryUid>
<settings.season>
<exclude>0</exclude>
<label>Saison:</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">Winter</numIndex>
<numIndex index="1">w</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">Sommer</numIndex>
<numIndex index="1">s</numIndex>
</numIndex>
</items>
<minitems>1</minitems>
<maxitems>1</maxitems>
</config>
</settings.season>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<settings.teamListPID>
<exclude>0</exclude>
<label>Listenansicht PID</label>
<config>
<type>input</type>
<size>20</size>
<required>1</required>
</config>
</settings.teamListPID>
<settings.teamShowPID>
<exclude>0</exclude>
<label>Einzelansicht PID</label>
<config>
<type>input</type>
<size>20</size>
<required>1</required>
</config>
</settings.teamShowPID>
<settings.teamCategories>
<exclude>0</exclude>
<label>Auf Kategorie beschr&#xE4;nken</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<enableMultiSelectFilterTextfield>1</enableMultiSelectFilterTextfield>
<foreign_table>sys_category</foreign_table>
<minitems>0</minitems>
<maxitems>99</maxitems>
</config>
</settings.teamCategories>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
@@ -49,6 +49,96 @@ call_user_func(function () {
'Schneehöhen'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'country_detail',
'Land'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'region_teasergroup',
'Gebiet: Teaser'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'region_detail',
'Gebiet: Details'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'city_teasergroup',
'Ort: Teaser'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'city_detail',
'Ort: Details'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_teasergroup',
'Hotel: Teaser'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_teasergroup_nodate',
'Hotel: Teaser (terminunabhängig)'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_detail',
'Hotel: Details'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_detail_event',
'Hotel: Details (Event)'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'concept_teasergroup',
'Konzept: Teaser'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'concept_detail',
'Konzept: Details'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'teammembers',
'Team'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'faq',
'FAQ'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'maps',
'Übersichtskarte Hotels/Produkte'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'pricetable',
'Preistabelle'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_product_detail',
@@ -73,4 +163,88 @@ call_user_func(function () {
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_snowreport.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_country_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_country_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_country.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_region_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_region_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_region_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_region_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_city_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_city_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_city_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_city_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_teasergroup_nodate'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_teasergroup_nodate',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser_date_independent.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_detail_event'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_detail_event',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail_event.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_concept_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_concept_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_concept_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_faq'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_faq',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_faq.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_maps'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_maps',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_maps.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_pricetable'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_pricetable',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_pricetable.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_teammembers'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_teammembers',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_teammembers.xml'
);
});
+134
View File
@@ -1,6 +1,15 @@
<?php
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
use EP\EpProducts\Controller\ProductController;
use EP\EpProducts\Controller\CityController;
use EP\EpProducts\Controller\ConceptController;
use EP\EpProducts\Controller\CountryController;
use EP\EpProducts\Controller\FaqController;
use EP\EpProducts\Controller\HotelController;
use EP\EpProducts\Controller\MapController;
use EP\EpProducts\Controller\PricetableController;
use EP\EpProducts\Controller\RegionController;
use EP\EpProducts\Controller\TeamController;
use EP\EpProducts\Controller\SearchController;
use EP\EpProducts\Controller\SearchbarController;
use EP\EpProducts\Controller\SnowreportController;
@@ -32,6 +41,11 @@ $boot = function () {
// Client-side callers pass these parameters in POST bodies, which cHash never inspects.
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['requireCacheHashPresenceParameters'][] = '^tx_epproducts_ajax';
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask::class]['options']['tables']['tx_epproducts_log'] = [
'dateField' => 'time_micro',
'expirePeriod' => 180,
];
ExtensionUtility::configurePlugin(
'EpProducts',
'product_detail',
@@ -56,6 +70,126 @@ $boot = function () {
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'country_detail',
[
CountryController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'region_teasergroup',
[
RegionController::class => 'teasergroup',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'region_detail',
[
RegionController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'city_teasergroup',
[
CityController::class => 'teasergroup',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'city_detail',
[
CityController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'hotel_teasergroup',
[
HotelController::class => 'teasergroup',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'hotel_teasergroup_nodate',
[
HotelController::class => 'dateIndependentTeasergroup',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'hotel_detail',
[
HotelController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'hotel_detail_event',
[
HotelController::class => 'detailEvent',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'faq',
[
FaqController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'concept_teasergroup',
[
ConceptController::class => 'teasergroup',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'concept_detail',
[
ConceptController::class => 'detail',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'teammembers',
[
TeamController::class => 'list,show',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'maps',
[
MapController::class => 'general',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'pricetable',
[
PricetableController::class => 'event',
]
);
ExtensionUtility::configurePlugin(
'EpProducts',
'searchresult',
@@ -3,6 +3,7 @@
namespace EP\EpTheme\DataProcessing;
use EP\EpProducts\Traits\DbConnectionTrait;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
@@ -54,9 +55,9 @@ class ProductRatingProcessor implements DataProcessorInterface
'areaServed' => 'DE',
'availableLanguage' => 'German'
],
'logo' => 'https://www.ep-reisen.de/typo3conf/ext/ep_theme/Resources/Public/images/logo_wide.svg',
'logo' => 'https://www.ep-reisen.de/' . PathUtility::getPublicResourceWebPath('EXT:ep_theme/Resources/Public/images/logo_wide.svg', false),
'priceRange' => '€',
'image' => 'https://www.ep-reisen.de/typo3conf/ext/ep_theme/Resources/Public/images/logo_wide.svg',
'image' => 'https://www.ep-reisen.de/' . PathUtility::getPublicResourceWebPath('EXT:ep_theme/Resources/Public/images/logo_wide.svg', false),
'aggregateRating' => [
'@type' => 'AggregateRating',
'ratingValue' => number_format($totalAverage/$ratedCount, 2),
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace EP\EpTheme\Preview;
use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;
/**
* Page module previews of the theme's content elements, rendered from
* Templates/PreviewRenderer/<CType>.html as the TYPO3 10 drawItem hooks did
* (Classes/Hooks/PageLayoutView in a46918d), with the same template variables.
* Registered per CType in TCA (['types'][<CType>]['previewRenderer']).
*/
class ContentPreviewRenderer extends StandardContentPreviewRenderer
{
/**
* CType => [field holding the related uid, related table, template variable]
*/
protected const RELATED_RECORDS = [
'teaser_country' => ['tx_eptheme_context_country', 'tx_epproducts_domain_model_country', 'country'],
'teaser_region' => ['tx_eptheme_context_region', 'tx_epproducts_domain_model_region', 'region'],
'teaser_city' => ['tx_eptheme_context_city', 'tx_epproducts_domain_model_city', 'city'],
'teaser_hotel' => ['tx_eptheme_context_hotel', 'tx_epproducts_domain_model_hotel', 'hotel'],
'teaser_concept' => ['tx_eptheme_context_concept', 'tx_epproducts_domain_model_concept', 'concept'],
'teaser_product' => ['tx_eptheme_context_product', 'tx_epproducts_domain_model_product', 'product'],
'teaser_booking_link' => ['tx_eptheme_context_product', 'tx_epproducts_domain_model_product', 'product'],
];
public function renderPageModulePreviewContent(GridColumnItem $item): string
{
$row = $item->getRecord();
$cType = (string)$row['CType'];
$template = GeneralUtility::getFileAbsFileName(
'EXT:ep_theme/Resources/Private/Templates/PreviewRenderer/' . GeneralUtility::underscoredToUpperCamelCase($cType) . '.html'
);
if (!is_file($template)) {
return parent::renderPageModulePreviewContent($item);
}
$images = $this->getImages((int)$row['uid'], 'tt_content', 'image');
$variables = [
'data' => $row,
'flexForm' => GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray((string)($row['pi_flexform'] ?? '')),
'firstImage' => $images[0] ?? null,
'images' => $images,
];
$variables = array_replace($variables, $this->getAssignmentsForTemplate($cType, $row));
$view = GeneralUtility::makeInstance(ViewFactoryInterface::class)->create(new ViewFactoryData(
templatePathAndFilename: $template,
request: $item->getContext()->getCurrentRequest(),
));
$view->assignMultiple($variables);
return $view->render();
}
protected function getAssignmentsForTemplate(string $cType, array $row): array
{
if (isset(self::RELATED_RECORDS[$cType])) {
[$field, $table, $name] = self::RELATED_RECORDS[$cType];
$uid = (int)($row[$field] ?? 0);
$assignments = [
'images' => $this->getImages($uid, $table, 'teaser_images'),
$name => $uid > 0 ? BackendUtility::getRecord($table, $uid) : null,
];
if ($cType === 'teaser_product') {
$hotelUid = (int)($row['tx_eptheme_context_hotel'] ?? 0);
$assignments['hotel'] = $hotelUid > 0 ? BackendUtility::getRecord('tx_epproducts_domain_model_hotel', $hotelUid) : null;
}
return $assignments;
}
switch ($cType) {
case 'gmap':
$regionUid = (int)($row['tx_eptheme_context_region'] ?? 0);
$content = $regionUid > 0
? BackendUtility::getRecord('tx_epproducts_domain_model_region', $regionUid)
: BackendUtility::getRecord('tx_epproducts_domain_model_hotel', (int)($row['tx_eptheme_context_hotel'] ?? 0));
return ['content' => $content];
case 'disrupter':
// tx_eptheme_disrupter is an MM relation; the field itself only holds the count
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_eptheme_ttcontent_tx_eptheme_disrupter_mm');
$disrupterUid = (int)$queryBuilder->select('uid_foreign')
->from('tx_eptheme_ttcontent_tx_eptheme_disrupter_mm')
->where($queryBuilder->expr()->eq('uid_local', $queryBuilder->createNamedParameter((int)$row['uid'])))
->orderBy('sorting')
->setMaxResults(1)
->executeQuery()
->fetchOne();
return ['content' => $disrupterUid > 0 ? BackendUtility::getRecord('tx_eptheme_domain_model_disrupter', $disrupterUid) : null];
case 'lineup':
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_eptheme_domain_model_lineup');
$rows = $queryBuilder->select('*')
->from('tx_eptheme_domain_model_lineup')
->where($queryBuilder->expr()->eq('tt_content', $queryBuilder->createNamedParameter((int)$row['uid'])))
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
return ['rows' => $rows];
}
return [];
}
protected function getImages(int $uid, string $table, string $field): array
{
if ($uid <= 0) {
return [];
}
return GeneralUtility::makeInstance(FileRepository::class)->findByRelation($table, $field, $uid);
}
}
@@ -74,7 +74,7 @@ class ContainerViewHelper extends AbstractViewHelper
$content = $this->arguments['content'];
}
$classes = [];
if (\in_array($this->arguments['data']['colPos'], static::$columnsWithoutContainers, false)) {
if (\in_array($this->arguments['data']['colPos'] ?? null, static::$columnsWithoutContainers, false)) {
$classes[] = 'container';
}
if ($this->arguments['cssClasses'] !== null) {
@@ -88,8 +88,8 @@ class ContainerViewHelper extends AbstractViewHelper
$scaleBottom = static::$scale[$this->arguments['data']['space_after_class']];
$classes[] = sprintf('pb-%d', $scaleBottom);
}
if ('default' !== $this->arguments['data']['frame_class']) {
$classes[] = $this->arguments['data']['frame_class'];
if ('default' !== ($this->arguments['data']['frame_class'] ?? null)) {
$classes[] = $this->arguments['data']['frame_class'] ?? null;
}
if (count($classes) > 0) {
return sprintf('<div class="%s"%s>%s</div>', implode(' ', $classes), $this->arguments['rteContent'] ? ' data-rte-content' : '', $content);
@@ -1,64 +1,59 @@
<?xml version="1.0"?>
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<TCEforms>
<sheetTitle>Konfiguration</sheetTitle>
</TCEforms>
<type>array</type>
<el>
<buttons>
<section>1</section>
<type>array</type>
<el>
<container>
<title>Button</title>
<type>array</type>
<el>
<label>
<TCEforms>
<label>Label</label>
<config>
<type>input</type>
<size>32</size>
</config>
</TCEforms>
</label>
<link>
<TCEforms>
<exclude>1</exclude>
<label>Button</label>
<config>
<type>input</type>
<eval>trim</eval>
<size>30</size>
<wizards type="array">
<_PADDING type="integer">2</_PADDING>
<link type="array">
<type>popup</type>
<title>Link</title>
<icon>EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_link.gif</icon>
<module>
<name>wizard_link</name>
<urlParameters>
<mode>wizard</mode>
</urlParameters>
</module>
<JSopenParams>height=300,width=500,status=0,menubar=0,scrollbars=1</JSopenParams>
</link>
</wizards>
</config>
</TCEforms>
</link>
</el>
</container>
</el>
</buttons>
</el>
</ROOT>
</sDEF>
</sheets>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>Konfiguration</sheetTitle>
<type>array</type>
<el>
<buttons>
<section>1</section>
<type>array</type>
<el>
<container>
<title>Button</title>
<type>array</type>
<el>
<label>
<label>Label</label>
<config>
<type>input</type>
<size>32</size>
</config>
</label>
<link>
<exclude>1</exclude>
<label>Button</label>
<config>
<type>input</type>
<eval>trim</eval>
<size>30</size>
<wizards type="array">
<_PADDING type="integer">2</_PADDING>
<link type="array">
<type>popup</type>
<title>Link</title>
<icon>EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_link.gif</icon>
<module>
<name>wizard_link</name>
<urlParameters>
<mode>wizard</mode>
</urlParameters>
</module>
<JSopenParams>height=300,width=500,status=0,menubar=0,scrollbars=1</JSopenParams>
</link>
</wizards>
</config>
</link>
</el>
</container>
</el>
</buttons>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
+21
View File
@@ -1,5 +1,6 @@
<?php
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
return [
@@ -7,4 +8,24 @@ return [
'provider' => SvgIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/logo.svg',
],
'tx_eptheme_gridelements_halves' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/tx_eptheme_gridelements_halves.png',
],
'tx_eptheme_gridelements_thirds' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/tx_eptheme_gridelements_thirds.png',
],
'tx_eptheme_gridelements_quarters' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/tx_eptheme_gridelements_quarters.png',
],
'tx_eptheme_gridelements_half_and_two_quarters' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/tx_eptheme_gridelements_half_and_two_quarters.png',
],
'tx_eptheme_gridelements_two_quarters_and_half' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ep_theme/Resources/Public/images/tx_eptheme_gridelements_two_quarters_and_half.png',
],
];
@@ -0,0 +1,46 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'Ansprechpartner',
'value' => 'contact',
'icon' => 'content-image'
],
'teaser_booking_link',
'after'
);
$GLOBALS['TCA']['tt_content']['columns']['tx_eptheme_contact'] = [
'exclude' => 0,
'label' => 'Mitarbeiter',
'config' => [
'type' => 'select',
'default' => 0,
'renderType' => 'selectSingle',
'foreign_table' => 'tx_epproducts_domain_model_teammember',
'foreign_table_where' => 'ORDER BY name',
'minitems' => 1,
'maxitems' => 1,
]
];
$GLOBALS['TCA']['tt_content']['types']['contact'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general,
tx_eptheme_contact,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
];
});
@@ -31,6 +31,35 @@ call_user_func(function () {
)
);
// Column layouts, migrated from gridelements in 2026 (commit 4172ed9). colPos = 200 + 10 × layout + column
$menus = 'menu_abstract,menu_categorized_content,menu_categorized_pages,menu_recently_updated,menu_related_pages,menu_section_pages,menu_sitemap,menu_sitemap_pages';
$columnContainers = [
'ep_container_halves' => ['Spalten 50/50', 'Zwei gleich breite Spalten', 'halves', [[201], [202]]],
'ep_container_thirds' => ['Spalten 33/33/33', 'Drei gleich breite Spalten', 'thirds', [[211], [212], [213]]],
'ep_container_columns_quarters' => ['Spalten 25/25/25/25', 'Vier gleich breite Spalten', 'quarters', [[221], [222], [223], [224]]],
'ep_container_half_and_two_quarters' => ['Spalten 50/25/25', 'Eine breite und zwei schmale Spalten', 'half_and_two_quarters', [[231, 'textmedia,teaser,list,form_formframework'], [232], [233]]],
'ep_container_two_quarters_and_half' => ['Spalten 25/25/50', 'Zwei schmale und eine breite Spalte', 'two_quarters_and_half', [[241], [242], [243, 'textmedia,teaser,list,gmap,nlform,form_formframework,menu_pages,menu_subpages,menu_section']]],
];
// No container inside these columns (topLevelLayout of the gridelements configuration)
$containers = implode(',', array_merge(['ep_container_quarters', 'ep_container_teasergroup'], array_keys($columnContainers)));
foreach ($columnContainers as $cType => [$label, $description, $icon, $columns]) {
$row = [];
foreach ($columns as $number => $column) {
$config = ['name' => 'Spalte ' . ($number + 1), 'colPos' => $column[0]];
if (isset($column[1])) {
$config['colspan'] = 2;
$config['allowed'] = ['CType' => $column[1]];
} else {
$config['disallowed'] = ['CType' => $menus . ',' . $containers];
}
$row[] = $config;
}
GeneralUtility::makeInstance(Registry::class)->configureContainer(
(new ContainerConfiguration($cType, $label, $description, [$row]))
->setIcon('tx_eptheme_gridelements_' . $icon)
);
}
$headerConfig = [
'showitem' => '
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.general,
@@ -47,7 +76,7 @@ call_user_func(function () {
'
];
foreach (['ep_container_quarters', 'ep_container_teasergroup'] as $field) {
foreach (array_merge(['ep_container_quarters', 'ep_container_teasergroup'], array_keys($columnContainers)) as $field) {
$GLOBALS['TCA']['tt_content']['types'][$field] = array_replace_recursive(
$GLOBALS['TCA']['tt_content']['types'][$field],
$headerConfig
@@ -0,0 +1,55 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'Störer (neu)',
'value' => 'disrupter',
'icon' => 'content-image'
],
'teaser_product',
'after'
);
$GLOBALS['TCA']['tt_content']['columns']['tx_eptheme_disrupter'] = [
'exclude' => 0,
'label' => 'Störer',
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'foreign_table' => 'tx_eptheme_domain_model_disrupter',
'maxitems' => 1,
'minitems' => 1,
'foreign_table_where' => 'ORDER BY headline ASC',
'MM' => 'tx_eptheme_ttcontent_tx_eptheme_disrupter_mm',
],
];
$GLOBALS['TCA']['tt_content']['types']['disrupter'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general, tx_eptheme_disrupter,
--div--;' . $frontendLanguageFilePrefix . 'tabs.appearance,
layout;' . $frontendLanguageFilePrefix . 'layout_formlabel, tx_eptheme_background,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
'columnsOverrides' => [
]
];
ExtensionManagementUtility::addPiFlexFormValue(
'*',
'FILE:EXT:ep_theme/Configuration/FlexForms/flexform_disturber.xml',
'disrupter'
);
});
@@ -0,0 +1,30 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'FAQ Alle Kategorien',
'value' => 'faq_overview',
'icon' => 'content-ep_theme-ce',
],
'lineup',
'after'
);
$GLOBALS['TCA']['tt_content']['types']['faq_overview'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
];
});
@@ -0,0 +1,32 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'Google Map',
'value' => 'gmap',
'icon' => 'content-image'
],
'ytvideo',
'after'
);
$GLOBALS['TCA']['tt_content']['types']['gmap'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general,
tx_eptheme_context_region, tx_eptheme_context_hotel,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
];
});
@@ -0,0 +1,52 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'Lineup',
'value' => 'lineup',
'icon' => 'content-image'
],
'gmap',
'after'
);
$GLOBALS['TCA']['tt_content']['columns']['tx_eptheme_lineup'] = [
'exclude' => 0,
'label' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang_db.xlf:tx_eptheme_domain_model_lineup.items',
'config' => [
'type' => 'inline',
'foreign_table' => 'tx_eptheme_domain_model_lineup',
'foreign_field' => 'tt_content',
'foreign_sortby' => 'sorting',
'maxitems' => 99,
'appearance' => [
'useSortable' => 1,
'collapseAll' => 1,
'levelLinksPosition' => 'top',
'showSynchronizationLink' => 0,
'showPossibleLocalizationRecords' => 0,
'showAllLocalizationLink' => 0
],
],
];
$GLOBALS['TCA']['tt_content']['types']['lineup'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general,
tx_eptheme_lineup,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
];
});
@@ -0,0 +1,40 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
call_user_func(function() {
$frontendLanguageFilePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:';
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'label' => 'YouTube/Vimeo Video',
'value' => 'ytvideo',
'icon' => 'content-image'
],
'textmedia',
'after'
);
$GLOBALS['TCA']['tt_content']['types']['ytvideo'] = [
'showitem' => '
--palette--;' . $frontendLanguageFilePrefix . 'palette.general;general,
layout, header_link,
--div--;' . $frontendLanguageFilePrefix . 'tabs.access,
hidden;' . $frontendLanguageFilePrefix . 'field.default.hidden,
--div--;' . $frontendLanguageFilePrefix . 'tabs.extended
',
'columnsOverrides' => [
'header_link' => [
'label' => 'YouTube URL/Vimeo ID',
],
'layout' => [
'label' => 'Typ',
],
],
];
});
@@ -0,0 +1,13 @@
<?php
use EP\EpTheme\Preview\ContentPreviewRenderer;
defined('TYPO3') or die();
// Page module previews (Templates/PreviewRenderer/<CType>.html); loads after tt_content_element_*.php
foreach (['teaser_country', 'teaser_region', 'teaser_city', 'teaser_hotel', 'teaser_concept', 'teaser_product',
'teaser_booking_link', 'gmap', 'disturber', 'disrupter', 'lineup'] as $cType) {
if (isset($GLOBALS['TCA']['tt_content']['types'][$cType])) {
$GLOBALS['TCA']['tt_content']['types'][$cType]['previewRenderer'] = ContentPreviewRenderer::class;
}
}
@@ -0,0 +1,198 @@
<?php
use TYPO3\CMS\Core\Resource\FileType;
return [
'ctrl' => [
'title' => 'Störer',
'default_sortby' => 'ORDER BY headline',
'label' => 'headline',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'dividers2tabs' => true,
'versioningWS' => true,
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => [
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
],
'searchFields' => 'headline,text',
'iconfile' => 'EXT:ep_theme/Resources/Public/images/icon_ce.svg',
],
'types' => [
'1' => [
'showitem' => 'sys_language_uid, hidden, layout, headline, text, image, pi_flexform',
],
],
'palettes' => [
],
'columns' => [
'sys_language_uid' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
'config' => ['type' => 'language'],
],
'l10n_parent' => [
'displayCond' => 'FIELD:sys_language_uid:>:0',
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
'config' => [
'type' => 'select',
'default' => 0,
'renderType' => 'selectSingle',
'items' => [
['label' => '', 'value' => 0],
],
'foreign_table' => 'tx_eptheme_domain_model_disrupter',
'foreign_table_where' => 'AND tx_eptheme_domain_model_disrupter.pid=###CURRENT_PID### AND tx_eptheme_domain_model_disrupter.sys_language_uid IN (-1,0)',
],
],
'l10n_diffsource' => [
'config' => [
'type' => 'passthrough',
],
],
't3ver_label' => [
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.versionLabel',
'config' => [
'type' => 'input',
'size' => 30,
'max' => 255,
]
],
'hidden' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden',
'config' => [
'type' => 'check',
],
],
'starttime' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
'config' => [
'type' => 'datetime',
'size' => 13,
'checkbox' => 0,
'default' => 0,
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
],
'endtime' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
'config' => [
'type' => 'datetime',
'size' => 13,
'checkbox' => 0,
'default' => 0,
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
],
'headline' => [
'exclude' => 0,
'label' => 'Headline',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'layout' => [
'exclude' => 0,
'label' => 'Layout',
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'items' => [
['label' => 'klein', 'value' => 0],
['label' => 'groß', 'value' => 1],
['label' => 'Event klein', 'value' => 2],
['label' => 'Event groß', 'value' => 3],
],
],
],
'text' => [
'exclude' => 0,
'label' => 'Text',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 5,
'eval' => 'trim',
'enableRichtext' => true,
],
],
'image' => [
'exclude' => 0,
'label' => 'Bild',
'config' => [
'type' => 'file',
'allowed' => 'jpg,jpeg',
'appearance' => [
'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference',
'fileUploadAllowed' => false,
],
'overrideChildTca' => [
'types' => [
FileType::IMAGE->value => [
'showitem' => '
--palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
--palette--;;filePalette'
],
],
'columns' => [
'crop' => [
'config' => [
'cropVariants' => [
'default' => [
'title' => 'Alle Geräte',
'allowedAspectRatios' => [
'square' => [
'title' => 'quadratisch',
'value' => 1,
],
'free' => [
'title' => 'Frei',
'value' => 'NaN',
],
],
],
],
],
],
],
],
'maxitems' => 1,
'minitems' => 1,
],
],
'pi_flexform' => [
'exclude' => 0,
'label' => 'Buttons',
'config' => [
'type' => 'flex',
'ds' => [
'default' => 'FILE:EXT:ep_theme/Configuration/FlexForms/flexform_disturber.xml',
],
]
],
'tt_content' => [
'exclude' => 0,
'config' => [
'type' => 'passthrough',
],
],
],
];
@@ -0,0 +1,210 @@
<?php
use TYPO3\CMS\Core\Resource\FileType;
return [
'ctrl' => [
'title' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang_db.xlf:tx_eptheme_domain_model_lineup',
'default_sortby' => 'ORDER BY name',
'label' => 'name',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'dividers2tabs' => true,
'versioningWS' => true,
'hideTable' => true,
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => [
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
],
'searchFields' => 'headline,subline',
'iconfile' => 'EXT:ep_theme/Resources/Public/images/icon_ce.svg',
],
'types' => [
'1' => [
'showitem' => 'hidden, name, description, image, --palette--;;social',
],
],
'palettes' => [
'social' => ['showitem' => 'facebook_url, twitter_url, youtube_url, instagram_url, soundcloud_url'],
],
'columns' => [
'sys_language_uid' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
'config' => ['type' => 'language'],
],
'l10n_parent' => [
'displayCond' => 'FIELD:sys_language_uid:>:0',
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
'config' => [
'type' => 'select',
'default' => 0,
'renderType' => 'selectSingle',
'items' => [
['label' => '', 'value' => 0],
],
'foreign_table' => 'tx_eptheme_domain_model_lineup',
'foreign_table_where' => 'AND tx_eptheme_domain_model_lineup.pid=###CURRENT_PID### AND tx_eptheme_domain_model_lineup.sys_language_uid IN (-1,0)',
],
],
'l10n_diffsource' => [
'config' => [
'type' => 'passthrough',
],
],
't3ver_label' => [
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.versionLabel',
'config' => [
'type' => 'input',
'size' => 30,
'max' => 255,
]
],
'hidden' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden',
'config' => [
'type' => 'check',
],
],
'starttime' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
'config' => [
'type' => 'datetime',
'size' => 13,
'checkbox' => 0,
'default' => 0,
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
],
'endtime' => [
'exclude' => 0,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
'config' => [
'type' => 'datetime',
'size' => 13,
'checkbox' => 0,
'default' => 0,
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
],
'sorting' => [
'config' => [
'type' => 'passthrough'
],
],
'name' => [
'exclude' => 0,
'label' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang_db.xlf:tx_eptheme_domain_model_lineup.name',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim,required'
],
],
'description' => [
'exclude' => 0,
'label' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang.xlf:tx_eptheme_domain_model_lineup.description',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 10,
'eval' => 'trim',
'enableRichtext' => true,
],
],
'image' => [
'exclude' => 0,
'label' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang_db.xlf:tx_eptheme_domain_model_lineup.image',
'config' => [
'type' => 'file',
'allowed' => 'common-image-types',
'appearance' => [
'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference',
'useSortable' => false,
],
'overrideChildTca' => [
'types' => [
FileType::IMAGE->value => [
'showitem' => '
--palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
--palette--;;filePalette',
],
],
],
'maxitems' => 1
],
],
'facebook_url' => [
'exclude' => 0,
'label' => 'Facebook URL',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'twitter_url' => [
'exclude' => 0,
'label' => 'Twitter URL',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'instagram_url' => [
'exclude' => 0,
'label' => 'Instagram URL',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'youtube_url' => [
'exclude' => 0,
'label' => 'YouTube URL',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'soundcloud_url' => [
'exclude' => 0,
'label' => 'Soundcloud URL',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim'
],
],
'tt_content' => [
'exclude' => 0,
'label' => 'LLL:EXT:ep_theme/Resources/Private/Language/locallang_db.xlf:tx_eptheme_domain_model_lineup.tt_content',
'config' => [
'type' => 'select',
'default' => 0,
'renderType' => 'selectSingle',
'foreign_table' => 'tt_content',
'minitems' => 0,
'maxitems' => 1,
],
],
],
];
@@ -1,4 +1,3 @@
@import 'EXT:ep_theme/Configuration/TSconfig/Page/Extensions/GridElements.tsconfig'
@import 'EXT:ep_theme/Configuration/TSconfig/Page/Mod/SHARED.tsconfig'
@import 'EXT:ep_theme/Configuration/TSconfig/Page/Mod/WebLayout/BackendLayouts.tsconfig'
@import 'EXT:ep_theme/Configuration/TSconfig/Page/Mod/Wizards/NewContentElement.tsconfig'
@@ -74,14 +74,8 @@ mod {
description = Störer mit Bild, mehreren Buttons und Kontaktinfo
tt_content_defValues.CType = disrupter
}
deal_of_the_week {
iconIdentifier = content-ep_theme-ce
title = Deal der Woche
description = Störer 'Deal der Woche' mit link auf Produkt
tt_content_defValues.CType = deal_of_the_week
}
}
show := addToList(disturber, disrupter, deal_of_the_week)
show := addToList(disturber, disrupter)
}
special {
elements {
@@ -97,20 +91,8 @@ mod {
description = Links/rechts alternierende Boxen
tt_content_defValues.CType = lineup
}
event_pricetable {
iconIdentifier = content-ep_theme-ce
title = Event Preistabelle
description = Preistabelle nach Verfügbarkeit
tt_content_defValues.CType = event_pricetable
}
nlform {
iconIdentifier = content-ep_theme-ce
title = Newsletter Anmeldung
description = Anmeldeformular
tt_content_defValues.CType = nlform
}
}
show := addToList(faq_overview, lineup, event_pricetable, nlform)
show := addToList(faq_overview, lineup)
}
common {
elements {
@@ -126,12 +108,6 @@ mod {
description = Kontaktbox mit Ansprechpartner für Sidebar
tt_content_defValues.CType = contact
}
calendar {
iconIdentifier = content-ep_theme-ce
title = Belegungskalender
description = Belegungskalender zu einem ausgewählten Vertragspartner
tt_content_defValues.CType = calendar
}
ytvideo {
iconIdentifier = content-ep_theme-ce
title = YouTube Video
@@ -145,7 +121,7 @@ mod {
tt_content_defValues.CType = button
}
}
show := addToList(gmap, contact, calendar, ytvideo, button)
show := addToList(gmap, contact, ytvideo, button)
}
}
}
@@ -50,10 +50,14 @@ TCEFORM {
5 = Partnerlogos
}
}
gridelements_pi1 {
ep_container_halves {
disabled = 0
removeItems := addToList(1,2,3)
}
ep_container_thirds < .ep_container_halves
ep_container_columns_quarters < .ep_container_halves
ep_container_half_and_two_quarters < .ep_container_halves
ep_container_two_quarters_and_half < .ep_container_halves
teaser {
disabled = 0
removeItems := addToList(2,3)
@@ -114,7 +118,11 @@ TCEFORM {
space_before_class {
types {
gridelements_pi1.disabled = 1
ep_container_halves.disabled = 1
ep_container_thirds.disabled = 1
ep_container_columns_quarters.disabled = 1
ep_container_half_and_two_quarters.disabled = 1
ep_container_two_quarters_and_half.disabled = 1
form_formframework.disabled = 1
}
removeItems := addToList(extra-small, medium, extra-large)
@@ -0,0 +1,23 @@
tt_content.contact =< lib.contentElement
tt_content.contact {
templateName = Contact
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_epproducts_domain_model_teammember
where.data = field:tx_eptheme_contact
where.wrap = uid=|
pidInList = {$plugin.tx_eptheme.settings.teamMemberStoragePid}
as = contact
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
10 {
references.fieldName = image
references.table = tx_epproducts_domain_model_teammember
as = image
}
}
}
}
}
@@ -1,3 +1,4 @@
# Column layouts (formerly gridelements)
tt_content.ep_container_halves =< lib.contentElement
tt_content.ep_container_halves {
templateName = Halves
@@ -20,6 +21,40 @@ tt_content.ep_container_thirds {
}
}
tt_content.ep_container_columns_quarters =< lib.contentElement
tt_content.ep_container_columns_quarters {
templateName = ColumnsQuarters
templateRootPaths {
10 = EXT:ep_theme/Resources/Private/Templates/Container
}
dataProcessing {
100 = B13\Container\DataProcessing\ContainerProcessor
}
}
tt_content.ep_container_half_and_two_quarters =< lib.contentElement
tt_content.ep_container_half_and_two_quarters {
templateName = HalfAndTwoQuarters
templateRootPaths {
10 = EXT:ep_theme/Resources/Private/Templates/Container
}
dataProcessing {
100 = B13\Container\DataProcessing\ContainerProcessor
}
}
tt_content.ep_container_two_quarters_and_half =< lib.contentElement
tt_content.ep_container_two_quarters_and_half {
templateName = TwoQuartersAndHalf
templateRootPaths {
10 = EXT:ep_theme/Resources/Private/Templates/Container
}
dataProcessing {
100 = B13\Container\DataProcessing\ContainerProcessor
}
}
# Teaser rows
tt_content.ep_container_quarters =< lib.contentElement
tt_content.ep_container_quarters {
templateName = Quarters
@@ -0,0 +1,28 @@
tt_content.disrupter =< lib.contentElement
tt_content.disrupter {
templateName = Disrupter
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_eptheme_domain_model_disrupter
pidInList = {$plugin.tx_eptheme.settings.disrupterStoragePid}
join = tx_eptheme_ttcontent_tx_eptheme_disrupter_mm ON tx_eptheme_ttcontent_tx_eptheme_disrupter_mm.uid_foreign = tx_eptheme_domain_model_disrupter.uid
where {
data = field:uid
intval = 1
wrap = tx_eptheme_ttcontent_tx_eptheme_disrupter_mm.uid_local=|
}
as = disrupter
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
10 {
references.fieldName = image
references.table = tx_eptheme_domain_model_disrupter
as = image
}
20 = EP\EpTheme\DataProcessing\FlexFormProcessor
}
}
}
}
@@ -0,0 +1,17 @@
tt_content.faq_overview =< lib.contentElement
tt_content.faq_overview {
templateName = FaqOverview
settings {
infoPageUid = {$plugin.tx_eptheme.settings.infoPageUid}
}
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_epproducts_domain_model_faq
where = category != ''
orderBy = category ASC, uid ASC
pidInList = {$plugin.tx_epproducts.settings.faqsStoragePid}
as = faqs
}
}
}
@@ -0,0 +1,23 @@
tt_content.gmap =< tt_content.teaser_product
tt_content.gmap {
templateName = Gmap
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_epproducts_domain_model_hotel
where.data = field:tx_eptheme_context_hotel
where.wrap = uid=|
pidInList = {$plugin.tx_epproducts.settings.hotelsStoragePid}
as = hotel
}
20 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
20 {
table = tx_epproducts_domain_model_region
where.data = field:tx_eptheme_context_region
where.wrap = uid=|
pidInList = {$plugin.tx_epproducts.settings.destinationsStoragePid}
recursive = 2
as = region
}
}
}
@@ -0,0 +1,24 @@
tt_content.lineup =< lib.contentElement
tt_content.lineup {
templateName = Lineup
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_eptheme_domain_model_lineup
where.data = field:uid
where.wrap = tt_content=|
orderBy = sorting
as = items
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
10 {
references.fieldName = image
references.table = tx_eptheme_domain_model_lineup
as = image
}
}
}
}
}
@@ -0,0 +1,7 @@
# Retired 2026-10-24 (newsletter form and subscription, reseller export, group price calculator, which
# MyE&P replaced): the elements stay in the database and render nothing; the old code is in a46918d
# should they come back
tt_content.nlform = TEXT
tt_content.list.20.epproducts_newsletter_subscription = TEXT
tt_content.list.20.epproducts_reseller = TEXT
tt_content.list.20.epproducts_hotel_groups_price = TEXT
@@ -0,0 +1,4 @@
tt_content.ytvideo =< lib.contentElement
tt_content.ytvideo {
templateName = YoutubeVideo
}
@@ -10,4 +10,6 @@ lib.contentElement {
settings.mapTileUrl = {$plugin.tx_eptheme.settings.mapTileUrl}
settings.mapApiKey = {$plugin.tx_eptheme.settings.mapApiKey}
settings.mapAttribution = {$plugin.tx_eptheme.settings.mapAttribution}
# As on TYPO3 10: headers with the default layout are not shown (editors rely on it)
settings.defaultHeaderType =
}
@@ -0,0 +1,15 @@
@import 'EXT:news/Configuration/TypoScript/setup.typoscript'
plugin.tx_news {
view {
layoutRootPaths.100 = EXT:ep_theme/Resources/Private/Layouts/News/
templateRootPaths.100 = EXT:ep_theme/Resources/Private/Templates/News/
partialRootPaths.100 = EXT:ep_theme/Resources/Private/Partials/News/
}
settings {
tagListPageUid = {$plugin.tx_eptheme.settings.blogTagListPageUid}
# The TYPO3 10 paginate widget showed 3 page links; NumberedPagination is not installed
list.paginate.class = TYPO3\CMS\Core\Pagination\SlidingWindowPagination
list.paginate.maximumNumberOfLinks = 3
}
}
@@ -42,29 +42,30 @@ page {
headerData.333 = TEXT
headerData.333.value (
<link rel="shortcut icon" type="image/x-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon.ico">
<link rel="icon" type="image/x-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon.ico">
<link rel="icon" type="image/gif" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon.gif">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon.png">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon.png">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-57x57.png" sizes="57x57">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-60x60.png" sizes="60x60">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-72x72.png" sizes="72x72">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-76x76.png" sizes="76x76">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-114x114.png" sizes="114x114">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-120x120.png" sizes="120x120">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-128x128.png" sizes="128x128">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-144x144.png" sizes="144x144">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-152x152.png" sizes="152x152">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-180x180.png" sizes="180x180">
<link rel="apple-touch-icon" href="/typo3conf/ext/ep_theme/Resources/Public/images/apple-touch-icon-precomposed.png">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="/typo3conf/ext/ep_theme/Resources/Public/images/favicon-196x196.png" sizes="196x196">
<link rel="shortcut icon" type="image/x-icon" href="/{path:EXT:ep_theme/Resources/Public/images/favicon.ico}">
<link rel="icon" type="image/x-icon" href="/{path:EXT:ep_theme/Resources/Public/images/favicon.ico}">
<link rel="icon" type="image/gif" href="/{path:EXT:ep_theme/Resources/Public/images/favicon.gif}">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon.png}">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon.png}">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-57x57.png}" sizes="57x57">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-60x60.png}" sizes="60x60">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-72x72.png}" sizes="72x72">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-76x76.png}" sizes="76x76">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-114x114.png}" sizes="114x114">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-120x120.png}" sizes="120x120">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-128x128.png}" sizes="128x128">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-144x144.png}" sizes="144x144">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-152x152.png}" sizes="152x152">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-180x180.png}" sizes="180x180">
<link rel="apple-touch-icon" href="/{path:EXT:ep_theme/Resources/Public/images/apple-touch-icon-precomposed.png}">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-16x16.png}" sizes="16x16">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-32x32.png}" sizes="32x32">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-96x96.png}" sizes="96x96">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-160x160.png}" sizes="160x160">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-192x192.png}" sizes="192x192">
<link rel="icon" type="image/png" href="/{path:EXT:ep_theme/Resources/Public/images/favicon-196x196.png}" sizes="196x196">
)
headerData.333.insertData = 1
# disableCompression keeps ResourceCompressor's hands off the Encore entries: Webpack
# already minifies them, and copying them to typo3temp/assets/compressed only replaces the
@@ -212,6 +213,12 @@ page.headerData.990 >
page.headerData.995 >
[end]
# Comparisons with TYPO3 10 (.ddev/plans/checks): an element that fails renders the error placeholder
# instead of failing the page, so the other elements of the page can still be compared
[applicationContext == "Development" && traverse(request.getHeaders(), 'x-ep-compare/0') == '1']
config.contentObjectExceptionHandler = 1
[end]
#[applicationContext == "Development" || applicationContext == "Production/Staging"]
# page.headerData.990 >
# page.headerData.995 >
@@ -195,8 +195,7 @@ plugin.tx_eptheme {
@import 'EXT:fluid_styled_content/Configuration/TypoScript/constants.typoscript'
@import 'EXT:ep_products/Configuration/TypoScript/constants.typoscript'
@import 'EXT:gridelements/Configuration/TypoScript/constants.typoscript'
@import 'EXT:seo/Configuration/TypoScript/XmlSitemap/constants.typoscript'
@import 'EXT:typo3_encore/Configuration/TypoScript/constants.typoscript'
@import 'EXT:news/Configuration/TypoScript/constants.txt'
@import 'EXT:news/Configuration/TypoScript/constants.typoscript'
@import 'EXT:bw_captcha/Configuration/TypoScript/constants.typoscript'
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

@@ -0,0 +1,7 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:render section="content" />
</html>
@@ -0,0 +1,31 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSlider" arguments="{_all}"/>
</header>
<main id="main-content">
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else>
</f:if>
<f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
</f:else>
</f:if>
</aside>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,22 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="EventHero" arguments="{_all}"/>
<f:render partial="EventDisturber" arguments="{headline: settings.eventDisturberHeadline, listItems: settings.eventDisturberListitems, buttonLabel: settings.eventDisturberButtonLabel, buttonLink: settings.eventDisturberButtonLink}"/>
</header>
<main id="main-content">
<f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
</aside>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,7 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render section="Content"/>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,35 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else>
</f:if>
</header>
<main id="main-content">
<f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-1 order-last lg:order-none">
<f:render section="Sidebar"/>
</div>
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
</div>
<f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,29 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/>
</header>
<main id="main-content">
<f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-1 order-last lg:order-none">
<f:render section="Sidebar"/>
</div>
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
</div>
<f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,29 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/>
</header>
<main id="main-content">
<f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
<div class="col-span-3 lg:col-span-1">
<f:render section="Sidebar"/>
</div>
</div>
<f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
</html>
@@ -0,0 +1,52 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<div class="relative flex flex-col">
<div class="relative">
{ep:icon(icon: 'flag-{city.country.code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6 absolute top-0 left-0')}
<f:alias map="{teaserImage: city.teaserImage, defaultImage: city.images.0}">
<f:if condition="{teaserImage}">
<f:then>
<f:variable name="image" value="{teaserImage}"/>
</f:then>
<f:else>
<f:variable name="image" value="{defaultImage}"/>
</f:else>
</f:if>
<f:if condition="{image}">
<f:then>
<f:image image="{image}"
loading="lazy"
class="block w-full h-full object-cover"
width="400c"
height="300c"
alt="{city.name}" />
</f:then>
<f:else>
<f:image src="EXT:ep_theme/Resources/Public/images/lazy.png"
width="400c"
height="300c"
alt="{city.name}"/>
</f:else>
</f:if>
</f:alias>
</div>
<div class="flex-1 flex flex-col pt-4 bg-white border border-t-0 border-zinc-200">
<a href="{f:uri.typolink(parameter: city.detailPage)}" class="px-2 peer headline--3 mb-4 no-underline">
{city.name}
<span class="absolute inset-0"></span>
</a>
<div class="flex-1 px-2 text-zinc-800" data-rte-content>
{city.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}
</div>
<div class="button teaser-button">
{f:translate(key: 'tx_eptheme.label.teaserbutton.default', extensionName: 'ep_theme')}
</div>
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,51 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<f:if condition="{concept}">
<div class="relative flex flex-col">
<div class="relative">
<f:alias map="{teaserImage: concept.teaserImage, defaultImage: concept.headerImage}">
<f:if condition="{teaserImage}">
<f:then>
<f:variable name="image" value="{teaserImage}"/>
</f:then>
<f:else>
<f:variable name="image" value="{defaultImage}"/>
</f:else>
</f:if>
<f:if condition="{image}">
<f:then>
<f:image image="{image}"
loading="lazy"
class="block w-full h-full object-cover"
width="400c"
height="300c"
alt="{concept.name}" />
</f:then>
<f:else>
<f:image src="EXT:ep_theme/Resources/Public/images/lazy.png"
width="400c"
height="300c"
alt="{concept.name}"/>
</f:else>
</f:if>
</f:alias>
<div class="absolute z-10 inset-0 bg-concept-{concept.code}/70">
<f:link.typolink parameter="{concept.detailPage}"
class="flex h-full w-full p-4 items-end headline--3 text-white">
{concept.name}
<span class="absolute inset-0"></span>
</f:link.typolink>
</div>
</div>
<div class="p-4 flex-1">
{concept.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}
</div>
<div class="bg-concept-{concept.code} h-1 w-20"></div>
</div>
</f:if>
</f:section>
</html>
@@ -0,0 +1,63 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="{ep:themeClasses(classes: '{ep: \'bg-ep-primary-dark\', sbw: \'bg-sbw-primary/80\', snz: \'bg-snz-primary/80\', suz: \'bg-suz-primary/80\', uch: \'bg-uch-primary/80\'}', key: settings.themekey)} my-2">
<f:if condition="{settings.themeKey} == 'ep'">
<f:render partial="Disrupter/Outlines"
section="Top1"
arguments="{classes: 'hidden md:block -translate-y-2 {f:if(condition: \'{data.tx_eptheme_background} == 1 || {data.tx_eptheme_background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
</f:if>
<div class="relative container grid grid-cols-2 gap-8 py-4">
<div class="col-span-2 md:col-span-1">
<div class="relative max-w-md mx-auto mb-8">
<f:if condition="{image}">
<f:image image="{image}"
loading="lazy"
class="block w-full h-auto rounded-full"
width="400"
height="400c"
alt="{image.alternative}" />
</f:if>
<a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}"
rel="nofollow"
class="absolute bottom-0 left-1/2 -translate-x-1/2 -mb-8 rounded-full py-4 px-6 flex items-center space-x-2 {ep:themeClasses(classes: '{ep: \'bg-ep-secondary\', sbw: \'bg-sbw-secondary\', snz: \'bg-snz-secondary\', suz: \'bg-suz-secondary\', uch: \'bg-uch-secondary\'}', key: settings.themekey)}">
{ep:icon(icon: 'phone', class: 'w-10 h-10 text-white')}
<div class="text-center text-white">
<span class="block whitespace-nowrap">
Ihr habt Fragen?
</span>
<span class="block whitespace-nowrap">
Wir helfen euch gerne!
</span>
</div>
</a>
</div>
</div>
<div class="col-span-2 md:col-span-1">
<span class="headline--secondary font-light text-white">{data.subheadline}</span>
<span class="headline--secondary uppercase font-light text-white mb-4">{data.headline}</span>
<div class="text-white" data-rte-content>
{data.bodytext -> f:format.html()}
</div>
<f:if condition="{data.flexForm.buttons}">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 lg:gap-8">
<f:for each="{data.flexForm.buttons}" as="button">
<f:link.typolink parameter="{button.container.link}"
class="button bg-button--secondary shadow-lg">
{button.container.label}
</f:link.typolink>
</f:for>
</div>
</f:if>
</div>
</div>
<f:if condition="{settings.themeKey} == 'ep'">
<f:render partial="Disrupter/Outlines"
section="Bottom1"
arguments="{classes: 'hidden md:block translate-y-2 {f:if(condition: \'{data.tx_eptheme_background} == 2 || {data.tx_eptheme_background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
</f:if>
</div>
</html>
@@ -0,0 +1,29 @@
<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:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="bg-ep-primary-dark my-2">
<f:render partial="Disrupter/Outlines"
section="Top1"
arguments="{classes: 'hidden md:block -translate-y-2 {f:if(condition: \'{data.tx_eptheme_background} == 1 || {data.tx_eptheme_background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
<div class="max-w-screen-lg mx-auto" data-rte-content>
<div>
{data.headline}
</div>
<div>
{data.text -> f:format.html()}
</div>
<div>
{data.flexForm.buttons -> v:iterator.first() -> v:variable.set(name: 'button')}
<a href="{f:uri.typolink(parameter: button.container.link)}" class="btn btn-info">
{button.container.label -> f:format.raw()}
</a>
</div>
</div>
<f:render partial="Disrupter/Outlines"
section="Bottom1"
arguments="{classes: 'hidden md:block translate-y-2 {f:if(condition: \'{data.tx_eptheme_background} == 2 || {data.tx_eptheme_background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
</div>
</html>
@@ -0,0 +1,30 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:alias map="{button: data.flexForm.buttons.0}">
<div class="container-fluid ep-disturber-reminder">
<div data-rte-content>
{data.headline}
</div>
<div class="col-md-6">
<a href="{f:uri.typolink(parameter: button.container.link)}" title="{button.container.label -> f:format.raw()}">
<f:if condition="{image}">
<f:image image="{image}"
loading="lazy"
class="block w-full h-auto rounded-full"
width="400"
alt="{image.alternative}" />
</f:if>
</a>
</div>
<div class="col-md-6" data-rte-content>
{data.text -> f:format.html()}
</div>
<a href="{f:uri.typolink(parameter: button.container.link)}" class="btn btn-info">
{button.container.label -> f:format.raw()}
</a>
</div>
</f:alias>
</html>
@@ -0,0 +1,42 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="{ep:themeClasses(classes: '{ep: \'bg-ep-primary-dark\', sbw: \'bg-sbw-primary/80\', snz: \'bg-snz-primary/80\', suz: \'bg-suz-primary/80\', uch: \'bg-uch-primary/80\'}', key: settings.themekey)} my-2">
<f:render partial="Disrupter/Outlines"
section="Top1"
arguments="{classes: 'hidden md:block -translate-y-2 {f:if(condition: \'{background} == 1 || {background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
<div class="max-w-screen-lg mx-auto py-4">
<div class="md:flex md:items-start py-4">
<div class="px-16 pb-4 md:px-4 md:pb-0 md:w-1/6">
<f:if condition="{image}">
<f:image image="{image}"
loading="lazy"
class="block w-full h-auto rounded-full"
width="400"
height="400c"
alt="{image.alternative}" />
</f:if>
</div>
<div class="px-16 md:px-4 md:w-5/6 md:pl-8 text-center text-white md:text-left" data-rte-content>
<span class="headline--3 text-white uppercase">{data.headline}</span>
{data.text -> f:format.html()}
<div class="flex flex-col space-y-4 md:flex-row md:items-center md:space-y-0 md:space-x-4">
<f:for each="{data.flexForm.buttons}" as="button" iteration="iteration">
<f:if condition="{iteration.cycle} <= 3">
<f:link.typolink parameter="{button.container.link}" class="button bg-button--secondary shadow-lg">
{button.container.label}
</f:link.typolink>
</f:if>
</f:for>
</div>
</div>
</div>
</div>
<f:render partial="Disrupter/Outlines"
section="Bottom1"
arguments="{classes: 'hidden md:block translate-y-2 {f:if(condition: \'{background} == 2 || {background} == 3\', then: \'text-ep-primary-bg\', else: \'text-white\')}'}"/>
</div>
</html>
@@ -0,0 +1,34 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<div class="container lg:max-w-screen-2xl my-8">
<div class="bg-pattern">
<div class="pt-8 lg:py-8 {ep:themeClasses(classes: '{sbw: \'bg-sbw-primary/80\', snz: \'bg-snz-primary/80\', suz: \'bg-suz-primary/80\', uch: \'bg-uch-primary/80\'}', key: settings.themekey)}">
<div class="flex flex-col space-y-4 lg:space-y-0 lg:flex-row items-center">
<div class="w-full lg:w-1/3 font-black uppercase text-white text-4xl lg:text-3xl text-center">
{headline -> f:format.raw()}
</div>
<div class="w-full lg:w-1/3 lg:px-4 text-center">
<ul class="list-none inline-flex lg:flex flex-col space-y-1 lg:space-y-2">
<f:for each="{listItems -> v:iterator.explode(glue: ';')}" as="listItem">
<li class="flex items-center space-x-2 text-white">
{ep:icon(icon: 'check', class: 'w-6 h-6')}
<span>{listItem -> f:format.raw()}</span>
</li>
</f:for>
</ul>
</div>
<div class="w-full lg:w-1/3 lg:px-4">
<a class="button bg-button w-full"
href="{f:uri.typolink(parameter: buttonLink)}">
{buttonLabel -> f:format.raw()}
</a>
</div>
</div>
</div>
</div>
</div>
</html>
@@ -0,0 +1,75 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="relative max-w-screen-2xl mx-auto">
<div class="absolute bottom-0 left-0 inset-x-0">
<div class="py-8 {ep:themeClasses(classes: '{sbw: \'bg-sbw-overlay/55\', snz: \'bg-snz-primary/50\', suz: \'bg-suz-primary/50\', uch: \'bg-uch-primary/50\'}', key: settings.themekey)}">
<div class="max-w-screen-md mx-auto px-4 md:px-0 flex">
<div class="hidden sm:flex flex-col justify-center {ep:themeClasses(classes: '{sbw: \'bg-sbw-primary\', snz: \'bg-snz-secondary\', suz: \'bg-suz-secondary\', uch: \'bg-uch-secondary\'}', key: settings.themekey)}">
<div class="text-white font-extrabold text-3xl lg:text-5xl flex-1 flex flex-col justify-around items-center text-center leading-none py-2 px-8 md:px-16">
<span>{settings.eventDateDayFrom -> f:format.raw()}</span>
<span>-</span>
<span>{settings.eventDateDayTo -> f:format.raw()}</span>
</div>
<div class="w-full px-4 bg-white text-center sbw:text-sbw-primary snz:text-snz-primary-dark suz:text-suz-primary-dark uch:text-uch-secondary p-2 whitespace-nowrap text-xl md:text-2xl uppercase font-bold leading-tight">
{settings.eventDateMonth -> f:format.raw()}
</div>
</div>
<div class="text-white uppercase text-3xl sm:text-5xl lg:text-7xl text-right sm:text-left font-extrabold px-4 md:px-8">
{settings.eventHeroClaim -> f:format.raw()}
</div>
<div class="text-white text-xl lg:text-2xl font-light border-l-4 md:border-l-8 border-white pl-4">
{settings.eventHeroDescription -> f:format.raw()}
</div>
</div>
</div>
<div class="sm:hidden text-white text-2xl uppercase text-center leading-none py-4 {ep:themeClasses(classes: '{sbw: \'bg-sbw-secondary\', snz: \'bg-snz-primary\', suz: \'bg-suz-primary\', uch: \'bg-uch-primary\'}', key: settings.themekey)}">
{settings.eventDateDayFrom -> f:format.raw()} - {settings.eventDateDayTo -> f:format.raw()} {settings.eventDateMonth -> f:format.raw()}
</div>
</div>
<f:alias map="{image: slide.image.0}">
<f:if condition="{image}">
<picture>
<source srcset="{f:uri.image(image: image, width: '1560')}" media="(min-width: 1280px)">
<source srcset="{f:uri.image(image: image, width: '1280')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: image, height: '1024', cropVariant: 'mobile')}" media="(max-width: 767px)">
<f:image class="block w-full h-full object-cover"
image="{image}"
height="1024"
cropVariant="mobile"
alt="{image.alternative}"/>
</picture>
</f:if>
</f:alias>
<f:if condition="{badge}">
<f:alias map="{badgeData: badge.0.data}">
<f:link.typolink parameter="{badgeData.link}">
<div class="absolute top-0 right-0 mt-8 mr-4">
<svg viewBox="0 0 200 200"
class="w-40 h-40">
<circle id="bgouter" cx="100" cy="100" r="100" fill="currentColor" class="{ep:themeClasses(classes: '{sbw: \'text-sbw-primary/50\', snz: \'text-snz-primary/50\', suz: \'text-suz-primary/50\', uch: \'text-uch-primary/50\'}', key: settings.themekey)}"/>
<circle id="bginner" cx="100" cy="100" r="70" fill="currentColor" class="{ep:themeClasses(classes: '{sbw: \'text-sbw-primary\', snz: \'text-snz-primary\', suz: \'text-suz-primary\', uch: \'text-uch-primary\'}', key: settings.themekey)}"/>
</svg>
<div class="absolute top-1/2 left-1/2 -translate-xy-1/2 flex flex-col items-center justify-around">
<span class="text-white uppercase font-extrabold text-center leading-tight">{badgeData.title -> f:format.raw()}</span>
<span class="text-white uppercase text-center leading-tight">{badgeData.subtitle -> f:format.raw()}</span>
</div>
</div>
</f:link.typolink>
</f:alias>
</f:if>
<f:comment><!--
<f:if condition="{settings.eventAddon}">
<f:link.typolink parameter="{settings.eventAddon.link}" class="{settings.eventAddon.class}"
title="{settings.eventAddon.title}">
<f:image src="{settings.eventAddon.file}"
alt="{settings.eventAddon.title}"
title="{settings.eventAddon.title}"/>
</f:link.typolink>
</f:if>
--></f:comment>
</div>
</html>
@@ -0,0 +1,40 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:if condition="{faqs}">
<div class="py-4" data-faq-target="category">
<f:if condition="{category}">
<div class="flex items-center space-x-2 pb-3">
<div class="bg-badge-gradient text-white rounded-full w-8 h-8 flex items-center justify-center">
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.45455 15L1 18.5V3C1 2.44772 1.44772 2 2 2H17C17.5523 2 18 2.44772 18 3V15H5.45455ZM4.76282 13H16V4H3V14.3851L4.76282 13ZM8 17H18.2372L20 18.3851V8H21C21.5523 8 22 8.44772 22 9V22.5L17.5455 19H9C8.44772 19 8 18.5523 8 18V17Z"></path></svg>
</div>
<span class="headline--4 uppercase block">{category}</span>
</div>
</f:if>
<f:for each="{faqs}" as="faq">
<div class="py-4"
data-controller="collapse"
data-collapse-collapsed-value="true"
data-faq-target="entry">
<button type="button"
aria-label="Antwort anzeigen/ausblenden"
class="text-lg md:text-xl flex items-center"
data-action="collapse#toggle">
{ep:icon(icon: 'chevron-right', class: 'w-6 h-6 transition-transform duration-200 ease-in-out', data: '{collapse-target: \'icon\'}')}
<span class="text-left">
{faq.data.question}
</span>
</button>
<div class="pt-2 pl-6 transition-transform ease-out overflow-hidden origin-top transform hidden max-w-screen-md"
data-collapse-target="container"
data-rte-content>
{faq.data.answer -> f:format.html()}
</div>
</div>
</f:for>
</div>
</f:if>
</html>
@@ -0,0 +1,87 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="splide mb-8"
data-controller="slider"
data-slider-preset-value="header">
<div class="splide__track">
<ul class="splide__list">
<f:for each="{slides}" as="slide" iteration="iteration">
<li class="splide__slide">
<div class="herounit">
<f:alias map="{image: slide.image.0}">
<f:if condition="{image}">
<div class="herounit__image">
<picture class="w-full h-full">
<source srcset="{f:uri.image(image: image, width: '1370')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: image, height: '640', cropVariant: 'mobile')}" media="(max-width: 767px)">
<img src="{f:uri.image(image: image, cropVariant: 'mobile', height: '640')}" class="w-full h-auto" alt="{image.alternative}">
</picture>
</div>
</f:if>
</f:alias>
<div class="absolute left-0 bottom-0 inset-x-0 px-4 py-8 w-full md:bottom-1/2 md:translate-y-1/2">
<div class="lg:container px-0">
<div class="p-4 bg-ep-primary-dark/80 sbw:bg-sbw-primary/80 snz:bg-snz-primary/80 suz:bg-suz-primary/80 uch:bg-uch-primary/80 ser:bg-ser-primary/80 mb-4 md:p-8 md:max-w-2xl">
<span class="block text-2xl md:text-4xl lg:text-5xl max-w-sm md:max-w-none font-black text-white uppercase mb-4 md:mb-8">
{slide.headline}
</span>
<f:if condition="{slide.subline}">
<span class="hidden lg:block md:text-lg text-white mb-4 md:mb-8">
{slide.subline}
</span>
</f:if>
<f:if condition="{slide.button}">
<f:link.typolink parameter="{slide.page_uid}"
additionalAttributes="{'tabindex': '-1'}"
class="button bg-badge-gradient hover:bg-none hover:bg-ep-pink text-white inline-block">
{slide.button}
</f:link.typolink>
</f:if>
</div>
</div>
</div>
<f:if condition="{slide.skipass} || {badge}">
<div class="herounit__badge-wrapper">
<f:if condition="{slide.skipass}">
<f:link.typolink parameter="{settings.skipassPageUid}"
additionalAttributes="{'tabindex': '-1'}">
<div class="herounit__badge herounit__badge--skipass">
<div class="uppercase text-4xl font-bold leading-7 mb-2">Ski-<br>Pass</div>
<div class="uppercase">inklusive</div>
</div>
</f:link.typolink>
</f:if>
<f:if condition="{badge}">
<f:alias map="{badgeData: badge.0.data}">
<f:link.typolink parameter="{badgeData.link}"
additionalAttributes="{'tabindex': '-1'}">
<div class="herounit__badge herounit__badge--custom">
<svg viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMinYMin meet"
class="w-full h-full">
<circle fill="currentColor" class="text-ep-primary-dark/50" cx="100" cy="100" r="100"/>
<circle fill="currentColor" class="text-ep-primary-dark" cx="100" cy="100" r="70"/>
</svg>
<div class="absolute top-0 left-0 inset-0 flex flex-col items-center justify-center p-8">
<span class="block text-center text-white bg-ep-primary-dark uppercase font-bold leading-none text-xl">{badgeData.title -> f:format.raw()}</span>
<f:if condition="{badgeData.subtitle}">
<span class="block text-center text-white bg-ep-primary-dark uppercase font-bold leading-none">{badgeData.subtitle -> f:format.raw()}</span>
</f:if>
</div>
</div>
</f:link.typolink>
</f:alias>
</f:if>
</div>
</f:if>
</div>
</li>
</f:for>
</ul>
</div>
</div>
</html>
@@ -0,0 +1,51 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:alias map="{images: '{media -> v:iterator.filter(propertyName: \'showinpreview\', filter: 0)}'}">
<f:if condition="{images}">
<f:if condition="{images -> f:count()} == 1">
<f:then>
<f:link.typolink parameter="{images.0.originalResource.link}" title="{images.0.title}">
<img src="{f:uri.image(image: images.0, width: '770', height: '430c+100')}"
class="block w-full h-auto mb-4"
alt="{images.0.alternative}">
</f:link.typolink>
</f:then>
<f:else>
<div class="splide mb-4"
data-controller="slider"
data-slider-preset-value="content">
<div id="carousel-{id}" class="splide__track">
<ul class="splide__list">
<f:for each="{images}" as="image" iteration="iteration">
<li class="splide__slide">
<f:link.typolink parameter="{image.originalResource.link}"
class="relative bg-cover bg-center" title="{image.title}">
<img src="{f:uri.image(image: image, width: '770', height: '430c+100')}"
class="block w-full h-auto"
alt="{image.alternative}">
</f:link.typolink>
</li>
</f:for>
</ul>
</div>
<f:if condition="{images -> f:count()} > 1">
<div class="hidden md:grid md:grid-cols-5 md:gap-2 md:pt-4 md:pb-8">
<f:for each="{images}" as="image" iteration="iteration">
<f:if condition="{iteration.cycle} <= 5">
<a href="#" class="block" data-slide="{iteration.index}" title="{image.title}">
<img src="{f:uri.image(image: image, width: '150', height: '85c+100')}"
class="block w-full h-auto" alt="{image.alternative}">
</a>
</f:if>
</f:for>
</div>
</f:if>
</div>
</f:else>
</f:if>
</f:if>
</f:alias>
</html>
@@ -0,0 +1,70 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="mb-8">
<n:excludeDisplayedNews newsItem="{newsItem}"/>
<div class="relative flex flex-col md:flex-row md:space-x-8">
<div class="mb-4 md:w-1/2">
<f:if condition="{newsItem.mediaPreviews}">
<f:then>
<f:if condition="{newsItem.mediaPreviews.0}">
<f:alias map="{mediaElement: '{newsItem.mediaPreviews.0}'}">
<img class="block w-full h-auto"
src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}"
loading="lazy"
alt="{mediaElement.originalResource.alternative}">
</f:alias>
</f:if>
</f:then>
<f:else>
<f:if condition="{newsItem.media}">
<f:alias map="{mediaElement: '{newsItem.media.0}'}">
<img class="block w-full h-auto"
src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}"
loading="lazy"
alt="{mediaElement.originalResource.alternative}">
</f:alias>
</f:if>
</f:else>
</f:if>
</div>
<div class="md:flex-1">
<n:link newsItem="{newsItem}" settings="{settings}" class="peer">
<h3 class="blog-entry__headline">
{newsItem.title}
</h3>
<span class="absolute inset-0"></span>
</n:link>
<p class="text-sm italic mb-4">
<f:if condition="{newsItem.author}">{newsItem.author} am </f:if>
<time datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}"><f:format.date format="{f:translate(key:'dateFormat')}">{newsItem.datetime}</f:format.date></time>
<f:if condition="{newsItem.tags}"> in
<f:for each="{newsItem.tags}" as="tag" iteration="iteration">
<f:link.page pageUid="{settings.tagListPageUid}" additionalParams="{tx_news_pi1:{overwriteDemand:{tags: tag}}}">
{tag.title}
</f:link.page>
{f:if(condition: iteration.isLast, else: ', ')}
</f:for>
</f:if>
</p>
<n:removeMediaTags>
<div class="mb-4">
<f:if condition="{newsItem.teaser}">
<f:then>
{newsItem.teaser -> f:format.crop(maxCharacters: '{settings.cropMaxCharacters}', respectWordBoundaries:'1') -> f:format.html()}
</f:then>
<f:else>
{newsItem.bodytext -> f:format.crop(maxCharacters: '{settings.cropMaxCharacters}', respectWordBoundaries:'1') -> f:format.html()}
</f:else>
</f:if>
</div>
</n:removeMediaTags>
<div class="button bg-button button--small">
<f:translate key="more-link"/>
</div>
</div>
</div>
</div>
</html>
@@ -0,0 +1,60 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:if condition="{pagination.lastPageNumber} > 1">
<f:if condition="{settings.list.paginate}">
<f:if condition="{pagination.previousPageNumber}">
<n:headerData><link rel="prev" href="{f:render(section: 'uri', arguments: {page: pagination.previousPageNumber})}" /></n:headerData>
</f:if>
<f:if condition="{pagination.nextPageNumber}">
<n:headerData><link rel="next" href="{f:render(section: 'uri', arguments: {page: pagination.nextPageNumber})}" /></n:headerData>
</f:if>
</f:if>
<nav aria-label="Paginierung">
<ul class="flex items-center space-x-2">
<f:if condition="{pagination.previousPageNumber}">
<li>
<a href="{f:render(section: 'uri', arguments: {page: pagination.previousPageNumber})}" class="button bg-button button--small">&laquo;</a>
</li>
</f:if>
<f:if condition="{pagination.displayRangeStart} > 1">
<li class="first">
<a href="{f:render(section: 'uri', arguments: {page: 1})}" class="button bg-button button--small">1</a>
</li>
</f:if>
<f:if condition="{pagination.hasLessPages}">
<li>....</li>
</f:if>
<f:for each="{pagination.allPageNumbers}" as="page">
<f:if condition="{page} == {paginator.currentPageNumber}">
<f:then>
<li class="active"><span class="button bg-button bg-button--active button--small">{page}</span></li>
</f:then>
<f:else>
<li>
<a href="{f:render(section: 'uri', arguments: {page: page})}" class="button bg-button button--small">{page}</a>
</li>
</f:else>
</f:if>
</f:for>
<f:if condition="{pagination.hasMorePages}">
<li class="disabled">....</li>
</f:if>
<f:if condition="{pagination.displayRangeEnd} < {pagination.lastPageNumber}">
<li class="last">
<a href="{f:render(section: 'uri', arguments: {page: pagination.lastPageNumber})}" class="button bg-button button--small">{pagination.lastPageNumber}</a>
</li>
</f:if>
<f:if condition="{pagination.nextPageNumber}">
<li class="last next">
<a href="{f:render(section: 'uri', arguments: {page: pagination.nextPageNumber})}" class="button bg-button button--small">&raquo;</a>
</li>
</f:if>
</ul>
</nav>
</f:if>
<f:section name="uri"><f:if condition="{page} > 1"><f:then>{f:uri.action(action: 'list', arguments: {currentPage: page}, addQueryString: 'untrusted')}</f:then><f:else>{f:uri.action(action: 'list', addQueryString: 'untrusted', argumentsToBeExcludedFromQueryString: {0: 'tx_news_pi1[currentPage]'})}</f:else></f:if></f:section>
</html>
@@ -0,0 +1,52 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<div class="relative flex flex-col">
<div class="relative">
{ep:icon(icon: 'flag-{region.country.code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6 absolute top-0 left-0')}
<f:alias map="{teaserImage: region.teaserImage, defaultImage: region.images.0}">
<f:if condition="{teaserImage}">
<f:then>
<f:variable name="image" value="{teaserImage}"/>
</f:then>
<f:else>
<f:variable name="image" value="{defaultImage}"/>
</f:else>
</f:if>
<f:if condition="{image}">
<f:then>
<f:image image="{image}"
loading="lazy"
class="block w-full h-full object-cover"
width="400c"
height="300c"
alt="{region.name}" />
</f:then>
<f:else>
<f:image src="EXT:ep_theme/Resources/Public/images/lazy.png"
width="400c"
height="300c"
alt="{region.name}"/>
</f:else>
</f:if>
</f:alias>
</div>
<div class="flex-1 flex flex-col pt-4 bg-white border border-t-0 border-zinc-200">
<a href="{f:uri.typolink(parameter: region.detailPage)}" class="px-2 peer headline--3 mb-4 no-underline">
{region.name}
<span class="absolute inset-0"></span>
</a>
<div class="flex-1 px-2 text-zinc-800" data-rte-content>
{region.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}
</div>
<div class="button teaser-button">
{f:translate(key: 'tx_eptheme.label.teaserbutton.default', extensionName: 'ep_theme')}
</div>
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,68 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="herounit h-80 lg:h-96 mb-8">
<f:alias map="{headerImage: images.0}">
<f:if condition="{headerImage}">
<picture>
<source srcset="{f:uri.image(image: headerImage, width: '1370c', height: '360')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: headerImage, width: '640c', height: '320')}" media="(max-width: 767px)">
<f:image class="block w-full h-full object-cover"
image="{headerImage}"
height="640"
alt="{headerImage.alternative}"/>
</picture>
</f:if>
</f:alias>
<div class="absolute bottom-0 left-0 inset-x-0 px-8 md:bottom-1/2 md:translate-y-1/2">
<div class="lg:container px-0">
<div class="bg-ep-primary/80 sbw:bg-sbw-primary/80 snz:bg-snz-primary/80 uch:bg-uch-primary/80 py-4 lg:py-8 px-4 mb-8 md:max-w-1/2 p-16">
<span class="block text-xl md:text-2xl font-light text-white uppercase">
{topline}
</span>
<span class="block text-2xl md:text-3xl lg:text-4xl text-white uppercase">
{headline}
</span>
</div>
</div>
</div>
<f:if condition="{headerImage.originaldescription}">
<div class="pagehead__picturecredit">{headerImage.description -> f:format.htmlspecialchars()}</div>
</f:if>
<f:if condition="{settings.showSkipassBadge} || {badge}">
<div class="herounit__badge-wrapper">
<f:if condition="{settings.showSkipassBadge}">
<f:link.typolink parameter="{settings.skipassPageUid}">
<div class="herounit__badge herounit__badge--skipass">
<p class="uppercase text-4xl font-bold leading-7 mb-2">Ski-<br>Pass</p>
<p class="uppercase">inklusive</p>
</div>
</f:link.typolink>
</f:if>
<f:if condition="{badge}">
<f:alias map="{badgeData: badge.0.data}">
<f:link.typolink parameter="{badgeData.link}">
<div class="herounit__badge herounit__badge--custom">
<svg viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMinYMin meet"
class="w-full h-full">
<circle fill="currentColor" class="text-ep-primary-dark/50" cx="100" cy="100" r="100"/>
<circle fill="currentColor" class="text-ep-primary-dark" cx="100" cy="100" r="70"/>
</svg>
<div class="absolute top-0 left-0 inset-0 flex flex-col items-center justify-center p-8">
<span class="block text-center text-white uppercase font-bold leading-none text-xl">{badgeData.title -> f:format.raw()}</span>
<f:if condition="{badgeData.subtitle}">
<span class="block text-center text-white uppercase font-bold leading-none">{badgeData.subtitle -> f:format.raw()}</span>
</f:if>
</div>
</div>
</f:link.typolink>
</f:alias>
</f:if>
</div>
</f:if>
</div>
</html>
@@ -0,0 +1,34 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<div class="relative flex flex-col">
<div class="relative">
<f:if condition="{context.country}">
{ep:icon(icon: 'flag-{context.country.code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6 absolute top-0 left-0')}
</f:if>
<f:image image="{teaserImage}"
loading="lazy"
class="block w-full h-full object-cover"
width="400c"
height="300c"
alt="{teaserImage.alternative}" />
</div>
<div class="flex-1 flex flex-col pt-4 bg-white border border-t-0 border-zinc-200">
<a href="{f:uri.typolink(parameter: data.header_link)}" class="px-2 peer headline--3 mb-2 no-underline">
{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}
<span class="absolute inset-0"></span>
</a>
<div class="flex-1 px-2 pb-6 text-zinc-800" data-rte-content>
{data.bodytext -> f:format.html()}
</div>
<div class="button teaser-button">
{data.button -> v:or(alternative: 'Details')}
</div>
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,27 @@
<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:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:section name="Teaser">
<div class="relative teaserbox teaserbox--default">
<div class="teasebox__image">
<f:render partial="TeaserImage" arguments="{image: teaserImage}"/>
</div>
<div class="teaserbox__main">
<a href="{f:uri.typolink(parameter: data.header_link)}" class="headline headline--3 headline--teaser no-underline">
{data.header -> f:format.nl2br()}{f:if(condition: data.subheader, then: '<br>{data.subheader}')}
<span class="absolute inset-0"></span>
</a>
<hr>
<div class="text-zinc-800" data-rte-content>
{data.bodytext -> f:format.html()}
</div>
</div>
<div class="button bg-button button--full">
{data.button -> v:or(alternative: 'Details')}
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,63 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:if condition="{image.uid}">
<f:then>
<picture>
<source media="(min-width: 1201px)" srcset="{f:uri.image(
image: image, width: '370', height: '220c-100'
)} 370w"/>
<source media="(max-width: 1200px)" srcset="{f:uri.image(
image: image, width: '320', height: '160c-100'
)} 320w"/>
<source media="(max-width: 991px)" srcset="{f:uri.image(
image: image, width: '360', height: '200c-100'
)} 360w"/>
<source media="(max-width: 768px)" srcset="{f:uri.image(
image: image, width: '500', height: '250c-100'
)} 500w"/>
<source media="(max-width: 512px)" srcset="{f:uri.image(
image: image, width: '350', height: '175c-100'
)} 350w"/>
<f:image image="{image}"
loading="lazy"
class="block w-full h-auto"
width="500"
height="250c-100"
alt="{image.alternative}" />
</picture>
</f:then>
<f:else>
<picture>
<source media="(min-width: 1201px)" srcset="{f:uri.image(
src: 'EXT:ep_theme/Resources/Public/images/header-winter.jpg',
width: '370', height: '220c-100'
)} 370w"/>
<source media="(max-width: 1200px)" srcset="{f:uri.image(
src: 'EXT:ep_theme/Resources/Public/images/header-winter.jpg',
width: '320', height: '160c-100'
)} 320w"/>
<source media="(max-width: 991px)" srcset="{f:uri.image(
src: 'EXT:ep_theme/Resources/Public/images/header-winter.jpg',
width: '360', height: '200c-100'
)} 360w"/>
<source media="(max-width: 768px)" srcset="{f:uri.image(
src: 'EXT:ep_theme/Resources/Public/images/header-winter.jpg',
width: '500', height: '250c-100'
)} 500w"/>
<source media="(max-width: 512px)" srcset="{f:uri.image(
src: 'EXT:ep_theme/Resources/Public/images/header-winter.jpg',
width: '350', height: '175c-100'
)} 350w"/>
<f:image src="EXT:ep_theme/Resources/Public/images/header-winter.jpg"
loading="lazy"
class="block w-full h-auto"
width="360"
height="270c-100"
alt="" />
</picture>
</f:else>
</f:if>
</html>
@@ -0,0 +1,31 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<f:render partial="SubpageHeader"
arguments="{images: city.headerImages, topline: city.headerSubtitle, headline: city.headerTitle, settings: settings}"/>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<div class="container">
<f:render partial="Header/Header"
arguments="{layout: 8, subheader: city.name, header: city.headline}"/>
<div class="grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-2">
<f:render partial="City/Details" arguments="{city: city, teasers: teasers}"/>
</div>
<div class="col-span-3 lg:col-span-1">
<f:render partial="Facts" arguments="{facts: city.facts, title: city.name}"/>
<f:if condition="{city.region.winter}">
<f:render partial="Snowreport" arguments="{snowreport: city.region.snowreport}"/>
<f:render partial="Regioninfo" arguments="{region: city.region}"/>
</f:if>
</div>
</div>
</div>
</f:section>
</html>
@@ -0,0 +1,46 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<ep:container data="{data}">
<f:if condition="{teasers}">
<div data-controller="teasergrid"
data-teasergrid-grid-class="grid-cols-2"
data-teasergrid-disabled-value="false">
<f:render partial="Header/Header"
arguments="{header: data.header, subheader: data.subheader, layout: data.header_layout, link: data.header_link, default: settings.defaultHeaderType}" />
<div class="flex items-center space-x-1 mb-2 sm:hidden">
<button type="button"
class="opacity-50"
aria-label="kompakte Ansicht"
data-action="teasergrid#grid"
data-teasergrid-target="button">
<svg class="w-12 h-12 text-ep-primary">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-teaser-grid"></use>
</svg>
</button>
<button type="button"
aria-label="vergrößerte Ansicht"
data-action="teasergrid#block"
data-teasergrid-target="button">
<svg class="w-12 h-12 text-ep-primary">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-teaser-single"></use>
</svg>
</button>
</div>
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8"
data-teasergrid-target="container">
<f:for each="{teasers}" as="city">
<f:render partial="City/Teaser" section="Teaser" arguments="{_all}"/>
</f:for>
</div>
</div>
</f:if>
</ep:container>
</f:section>
</html>
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff. Show more