Dates are written by the import only and are never translated (all rows
are in the default language). Like the matchcodes, the table loses
languageField, transOrigPointerField, transOrigDiffSourceField, its
language columns and the Language tab in TCA; the database columns stay
until the schema clean-up.
Checked locally: frontend crawl (~1,900 URLs incl. the English pages) and
the backchannel APIs render the same as before, the editor check creates
records in all other tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
New facts failed with "Field 'keywords' doesn't have a default value":
the column is TEXT NOT NULL without a database default and not part of
the form. It now gets a TCA default, like the passthrough text columns of
dates that are not in the form either.
Lineup items are inline children of the lineup content element on
standard pages. TYPO3 10 allowed the table there with
allowTableOnStandardPages(); the migration to
ctrl.security.ignorePageTypeRestriction only covered the slides, so
adding a lineup item failed with "Attempt to insert record on pages:...
where table is not allowed".
Checked locally as an editor: a new record of every table of ep_products
and ep_theme goes through FormEngine and DataHandler (dates excepted:
hidden, written by the import only).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Editors got "recordEditAccessInternals() check failed [ERROR: The
"languageField" field named "sys_language_uid" was not found in testing
record!]" for every new record of our tables, e.g. a hotel's additional
code or a new hotel. The record folders lie on the root level, outside
every site, and no form submitted the language field. TYPO3 10 then took
language 0 from sys_language; since TYPO3 11, DataHandler only takes the
default language from the record's site and sets none without a site, so
the access check for non-admins fails.
As stock TYPO3 expects it, the forms now submit the language: a Language
tab with sys_language_uid (default 0) and l10n_parent in the 27
language-aware tables of ep_products and ep_theme. Matchcodes (a hotel's
additional codes) are no longer translatable at all: none was ever
translated, and the feed, the API and the import read them without
language handling.
LabelService no longer reads from a missing record when the form of a
new record asks for a label.
Checked locally as an editor: FormEngine preselects language 0, and new
hotels, products, FAQs and additional codes are saved.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rector.php gets the typeDeclarations set: return types (void, array,
unions) where the code proves them, typed closures and arrow functions,
typed properties from constructors and assignments in the DTOs.
Skipped: rules that infer scalar or array parameter types from call
sites or docblocks, and the removal of casts the docblocks call
redundant. Values from the database, form input and the import feeds
arrive as strings and would throw instead of being converted (e.g. the
(int) cast of the newsletter list ids stays).
Checked locally: cache warmup, frontend crawl (~1,900 URLs), the
backchannel APIs and the newsletter form validation behave as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rector.php gets the codeQuality set: declare(strict_types=1) in the 85
files where Rector finds no call relying on scalar type juggling (mostly
TCA and configuration, models, the content feed API and the import
command), "or die" as "|| die", instanceof instead of null checks for
object types, early returns and self:: in final classes.
Hotel::setRooms() goes instead of getting the property Rector wanted to
declare: nothing calls it, and there is no rooms column any more.
Checked locally: cache warmup, the ep:* commands load, frontend crawl
(~1,900 URLs) and the backchannel APIs render the same as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rector.php gets the deadCode set: @param/@return/@var tags that only
repeat native types, === true comparisons, three unused constructor
dependencies (DateService, ProductDataService, AjaxWatchlistController),
the unused reseller lookup in ContextProcessor and an always-false check
in AbstractMappedFinisher (trimExplode with removeEmpty never yields an
empty remote name).
Checked locally: frontend crawl (~1,900 URLs) and the backchannel APIs
render the same as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Members (properties, methods, constants) become private when they are
declared in the class itself, no parent class or interface declares them,
no ancestor in packages/ uses them and the class has no subclass in
packages/: 115 members in 36 files. Extbase domain objects keep protected
properties, AbstractDomainObject sets them from the parent class.
With the properties private, Rector's constructor promotion now also
applies to the controllers and services that still assigned their
dependencies by hand (26 files).
Checked locally: frontend crawl (~1,900 URLs) and the backchannel APIs
render the same as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rector.php gets the PHP sets up to 8.5. Mostly constructor promotion with
readonly properties, typed class constants, ??=, arrow functions,
str_contains/str_starts_with, catch without unused variables, array_any
and #[\Override] on overridden properties; array_key_exists() keys are
cast to string (null keys are deprecated in PHP 8.5). Long promoted
constructor signatures are wrapped one parameter per line.
Checked locally: a crawl of ~1,900 frontend URLs over all sites and the
backchannel APIs (/api/hotel, /api/product, content feed manifest and
records of every type) render the same as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The image services cached under images-<table>-<uid> without the field, so
the teaser images (teaser_images, rendered by the frontend) and the full
image lists for MyE&P (images, /api/hotel and /api/product) shared one
cache entry. Once a page had shown a hotel teaser, the API answered with
that single teaser image instead of all images. The field is now part of
the key.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
f:image threw when the teaser had no image (teaserImage false), and
production showed "Oops, an error occurred!" instead of the element, e.g.
three times on the snowzone privacy page. The image is now skipped like in
the disturber partials.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The GPL headers named dreipunktnull and an old address and no longer
match the license of the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
It replaced the TYPO3 10 import tasks with scheduler tasks, which ran in
production on 2026-10-24 (tasks 26-28). The soft-deleted TYPO3 10 rows it
left are hard-deleted by hand on the database.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The form page type (/form, 1704) existed because on the static pages of the
hybrid setup the page URL never reached TYPO3. With static rendering
retired, forms post to the page they are on, like the newsletter form:
the additionalParams in FormSetup.yaml, Page/Form.typoscript and the /form
entries of the PageType enhancers are gone.
Each form island gets the id form-island-<form identifier> (EXT:form
appends the content element uid), and Form.html targets and selects that
id instead of .form-island. With two forms on one page, a submission no
longer swaps in the first form's island.
Kept: hx-boost on the forms, HtmxRedirectMiddleware (Redirect finishers
under htmx) and /captcha (bw_captcha's image type).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
They only served the static pages of the hybrid setup, which are retired:
TYPO3 13 renders search bar and snow reports in the page itself. Removed
are the page types 1705 (/snowreport) and 1706/1707 (/searchbar,
/searchbar-collapsed), their PageType enhancer entries and the id wrapper
of the snow report list that the static pages selected.
Also gone: the remarks about static pages and docroots in
ReferrerMiddleware and .htaccess.
The form page type (/form, 1704) stays for now; forms move off it in a
separate step.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The newsletter subscription plugin now sits in a form island like the
EXT:form forms: hx-boost posts to the page itself and only
#newsletter-island is swapped (validation errors, confirmation), with the
spinner overlay while the request runs. The id keeps htmx away from other
islands on the same page (both newsletter pages have hidden EXT:form
forms). Without JavaScript the form still posts normally.
The island markup moves from Form/Render.html into Partials/Form/Island.html,
shared by both; the form plugin's partial path is now Partials/ for that.
The comment about static pages is gone with it, static rendering is retired.
When the MyE&P API call fails, the result page shows an error with a link
back to the form instead of the thank-you message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The "Newsletter-/RA-Registrierung" plugin (epproducts_newsletter_subscription)
subscribes to the Mailjet lists through the MyE&P API and is still in use on
the newsletter, travel alert and travel for groups pages; retiring it in
49b96df was a mistake. Controller and templates come back from a46918d,
ported to TYPO3 13 (actions return responses). The DTO and the API client
calls were still in place. The "newsletter" result message plugin (index
action) is not part of this.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The select items of twelve plugin FlexForms were still numbered
<numIndex index="0">/<numIndex index="1"> pairs, and the team members
FlexForm set enableMultiSelectFilterTextfield, which TYPO3 12 removed (the
filter field is always shown now). The core migrated both on the fly on
every request and logged a deprecation each time ("FlexFormTools did an
on-the-fly migration"). The items are converted to <label>/<value> with
Fractor (MigrateItemsIndexedKeysToAssociativeFractor) and the setting is
removed.
The parsed data structures are identical to what the on-the-fly migration
produced, so the backend forms and stored values stay the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ski-boarderweek, snowzone and ser have a realfavicongenerator set in
fileadmin now, and their templates no longer set page.shortcutIcon, so
favicon_sbw.ico, favicon_sz.ico and favicon_ser.ico are unused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The theme registered 26 icons (16 to 196 px PNGs, a GIF, eleven Apple touch
icons) that current browsers no longer need. It now renders the five tags of
the set realfavicongenerator.net generates (96 px PNG, SVG, ICO, 180 px
Apple touch icon, web manifest) plus apple-mobile-web-app-title, from two
constants: faviconPath, the folder in fileadmin the set was uploaded to
(default: the E&P set), and faviconAppTitle.
The ICO also matches the favicon parser of yoast's snippet preview now
(rel="shortcut icon" directly followed by href), which missed the old tags
and fell back to a /favicon.ico none of the sites has.
cllt and alvac, which showed the theme's E&P icon, get the E&P set. Sites
whose own template sets page.shortcutIcon and removes headerData.333
(ski-boarderweek, snowzone, ser) keep their icon until they have a set; the
remaining ICOs of these three stay until then. ep-reisen and snowbreak still
override the tags with the same markup in their templates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The app config JSON carried "myEpApiBaseUrl" from the TypoScript setting
myEpApiEndpointUrl, which only the alvac and cllt sys_template records set:
the other sites rendered the unresolved "{$plugin.tx_eptheme.settings...}"
into the page. No JavaScript reads the key (only paCode and externalDomains
are used), and the MyE&P API client takes its URL, credentials and timeout
from the ep_products extension configuration, so the theme's
myEpApiEndpointUrl, myEpApiToken and myEpApiTimeout settings had no reader.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The verification code of alvac and cllt came from EXT:metaseo, which is not
installed in TYPO3 13, so their google-site-verification meta tag was
missing. The theme renders the new constant as
page.meta.google-site-verification; empty, it renders no tag. Sites whose
own setup sets the tag (ep-reisen, ski-boarderweek, snowbreak, snowzone, ser)
keep their value.
.ddev/plans/sql/2026-10-25-metaseo-sourceopt.sql renames the metaseo
constants to the new one after the deploy, removes the remaining metaseo
and sourceopt leftovers from the sys_template records, and sets the core
no_index/no_follow flags on the booking links (page 634 and subpages),
which metaseo had kept out of search engines.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
TYPO3 13 enforces a Content Security Policy in the backend whose img-src
only allows the backend's own host, data: URIs and the YouTube/Vimeo CDNs.
The frontend references its favicons on the CDN, so yoast's snippet preview
showed a broken favicon (CSP report: img-src blocked
https://cdn.ep-reisen.de/fileadmin/user_upload/favicon/ep/favicon.ico).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
settings.dateFrom and settings.dateTo were still type=input with eval=date.
TYPO3 13 only migrates renderType=inputDateTime to type=datetime, so these
were rendered as text inputs whose JavaScript expects an ISO 8601 value. It
got the stored timestamp (e.g. 1793491200), threw "Invalid ISO8601 DateTime
string", and the edit form of these plugin elements never finished loading.
The datetime element converts the stored timestamps itself and, without a
dbType, saves timestamps again, so stored values and the frontend stay as
they are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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]>
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]>
Restored from a46918d (the FAQ page /skireisen/infos/allgemeine-infos/faqs/);
markup matches TYPO3 10.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
TYPO3 13 lays out palette rows with more than four fields in sixth-width
columns and breaks the line after every second field, so the product form's
top palette showed up as a narrow column. Line breaks keep every row at four
fields or fewer (product, hotel, city).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The page holds only the travel info plugin, whose default action required
a travel info: opened without a slug or code it failed with "Required
argument travelInfo is not set" (TYPO3 10 did the same). The argument is
optional now and its absence gives the same 404 as an unknown slug or travel
code (notFound(), shared with travelCodeAction).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
21 of 53 office tips still point to team members who have left (deleted
records), so the backend label read 'name' from null: "Trying to access
array offset on null" in production. TYPO3 10 on PHP 7.4 printed an empty
name silently. The label is now the tip's text, with the author in brackets
only when the team member still exists; a tip record that cannot be found
(e.g. a new one) gives an empty title instead of a warning.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The search bar in the page header is back (SearchbarController, lib.searchBar
and lib.searchBarCollapsed), in the Subpage, SubpageSidebarRight and Detail
layouts as on TYPO3 10; product pages get the collapsed bar and its toggle
button in the main navigation again.
- The bar takes no arguments, so it is the same on every page of a site and
cacheable. Page types 1706/1707 (/searchbar, /searchbar-collapsed on the
ep-reisen root, also under /en/) render it alone for the static pages,
which replace their exported bar with it via htmx. The suffixes have no
leading slash: on the root page the route path has none, so "/searchbar"
would never match (as /form and /snowreport do not there either).
- The form sends dateFrom, dateTo, pax, destinationType, destinationUid and
priceRange by GET to the search page instead of POSTing to processSearch:
no signed fields and no redirect, and URLs of about 100 characters instead
of about 870. SearchController reads them (SEARCHBAR_PARAMETERS, excluded
from the cHash; the results are not cached) and drops anything but a real
Y-m-d date, which made DateUtility::formatDateRange() throw.
processSearch is removed.
- The date range label is empty without arguments, so the template no longer
needs ep:dateRange.
Compared with TYPO3 10: the bar's markup matches apart from the form (open,
collapsed, /en/); seven searches by GET load the same filter settings and
hit counts as the TYPO3 10 POST.
The static pages get the island from the exporter in EP/website, see
.ddev/plans/static-export-searchbar-island.patch (local only).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
29 hotels have category 0, which has no label. The product teasers looked
the label up unguarded, an "Undefined array key 0" warning: a 500 in DDEV
(warnings are exceptions there), a logged warning in production. The
teaser and the hotel serializer now use null for an unknown category, as
SearchResultService and HotelDataService already do; TYPO3 10 ended up
with null as well.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The snow report list ("Schneehöhen" plugin) is ported from TYPO3 10:
SnowreportController, SnowreportRepository::getSnowreportsForRegions(),
the plugin registration with its flexform (regions) and the template,
which now formats dates with d.m.Y.
Page type 1705 (/<page>/snowreport) renders only the snow report plugins
of a page, like /form for forms. The static pages of the hybrid setup can
load it to replace the snow reports frozen at export time; each list
carries id="snowreport-<content uid>" for hx-select, since TYPO3 10 gives
the plugin no element of its own. ep:import-snowreport already flushes
the page cache after each import. The suffix is in the PageType enhancer
of every site with snow reports.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
feat: the snow report box includes the spacing of its content element
id="snowreport-<content uid>" moves from the list to a wrapper around
ep:container. The static pages of the hybrid setup no longer export the
snow reports at all, only an empty placeholder that loads the whole box,
spacing included, from /<page>/snowreport, so a page never shows
outdated snow depths.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The .form-island wrapper existed only in the form page type (/form), so
on pages TYPO3 13 renders completely (e.g. the EP ambassador page) the
form's hx-target="closest .form-island" found nothing and submitting did
nothing. The island with its loading overlay now comes from the form
plugin's template (Render.html), on full pages and in /form alike; the
page type renders just the plugin again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>