Compare commits

..
35 Commits
Author SHA1 Message Date
frommeandClaude Opus 5.5 81a8013e8a feat: ep_products plugins are content element types of their own
Preparation for TYPO3 14, which drops list_type. The plugins are
registered as CTypes named like their former list_type values
(epproducts_product_detail etc.); the form keeps the fields of the
"list" type, with the plugin options in place of the plugin selector. As content
element types they show the E&P logo of the extension manager
(icon ep-products-plugin) instead of the generic plugin icon.

The upgrade wizard epProductsPluginListTypeToCType moves the elements
and the tt_content:list_type permissions of backend groups. Until it has
run, list_type elements render through the new types (alias block in
ext_localconf.php), so the deploy alone changes nothing. Retired plugins
stay "list" elements.

The content-only page type, the content manifest and the two container
columns limited to "list" now treat the epproducts_* types as plugins.

Crawl of 1,912 URLs identical for old code and new code before and after
the wizard; rehearsed again on the prod dump of 2026-11-01 (1,924 URLs,
API snapshot identical). Rollout and rollback SQL: .ddev/plans/PLUGINS-CTYPE-ROLLOUT.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-01 14:31:35 +01:00
fromme 518f29b160 chore: install typo3 console extension 2026-10-31 10:39:13 +01:00
frommeandClaude Opus 5.5 5b37b37a78 fix: event price table no longer shows "0" for missing room types
Every hotel row renders a column for each configured pax count (1–10),
but most hotels only offer some of them. For a missing room type the
"not available" branch ran and ep:price() turned the null price into a
struck-through "0". The nested f:if there repeated the outer condition
and so never filtered anything.

Cells now only show a struck-through price when the room has one, and
stay empty otherwise. The bug already existed on TYPO3 10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 08:11:37 +01:00
frommeandClaude Opus 5.5 ab2b840dc5 feat: search result teasers list all bookable nights options
Search teasers showed only the nights of the cheapest offer, while
teaser groups list every option. They now list all nights options next
to the cheapest "ab" price, like teaser groups and the watchlist do.

All search filters are applied in SQL before the rows are aggregated,
so the list only holds options matching the active filters (e.g. the
duration filter).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 16:05:45 +01:00
frommeandClaude Opus 5.5 4cefbce87e fix: /api/product no longer returns another product for some codes
The product lookup also matched hotel aliases (matchcodes), joined on
the product uid. Matchcodes only exist for hotels, so a hotel alias
equal to the requested code matched whatever product had that hotel's
uid. With no ordering, the database picked one of the rows: locally
DPWGRU3 returned SnowZone and KHHMP returned Hotel Terrace instead of
Roncalli Düren and Gasthof Heidelberg. The other 313 product codes
return the same response as before.

Products are looked up by code only. Some codes exist on two products
(e.g. MVKPGW); ordering by uid makes the result deterministic.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 15:46:55 +01:00
frommeandClaude Opus 5.5 2bd663a9bf docs: CMS API and calendars
docs/cms-api.md describes the endpoints TYPO3 serves to MyE&P and the
chatbot (/api/hotel, /api/product, /api/calendar-hotels,
/api/content/*): key, parameters, responses, errors. It is the other
side of MyE&P's API consumer guide.

docs/calendars.md covers the price calendar, the Belegungskalender and
the iCal feed: which product gets which, the MyE&P endpoints behind
them, caching, troubleshooting, and the rule that a product's calendar
hotel must carry the BusPro code, not a group house's CMS code (which
broke three example trips).

The README replaces the TYPO3 base distribution boilerplate and links
both. The plugin renaming SQL in ep_products' README is obsolete since
the migration. The cmsApiKey description names /api/calendar-hotels.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 15:37:36 +01:00
frommeandClaude Opus 5.5 bf9afd143d feat: API lists the calendar hotels for the contingent sync of MyE&P
GET /api/calendar-hotels returns the calendar hotel of every visible
product, with the products using it and their season, behind the same
API key as /api/hotel and /api/product.

MyE&P only syncs contingent snapshots for its accommodations, so hotels
that are only offered in winter (e.g. DKI, DWW, ASCGR) have no
availability for the iCal feed and the availability calendar. With this
list MyE&P can sync every hotel code TYPO3 actually uses, without a
second list to maintain. The products and seasons are for a status
overview of which hotel supports iCal, availability and prices.

Hidden hotels are left out, as on the website (e.g. ASB).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 14:45:44 +01:00
frommeandClaude Opus 5.5 5d41660a9a feat: availability calendar in the product detail sidebar is back
TYPO3 10 showed a "Belegungskalender" in the sidebar of every product
with a calendar hotel. It was dropped when the price calendar arrived,
and the price calendar is limited to summer products, so winter
products showed no availability at all.

The sidebar calendar is shown for products with a calendar hotel that
are not summer products. AjaxCalendar::availability reads the
availability from MyE&P (contingents/availability, as the iCal feed
does) instead of bpn-connect, and renders all months in one cacheable
response; the month select only switches between them. Months with
prices in the price config (summer, covered by the price calendar) are
left out. If the price config is unavailable, all months are shown
without caching.

The plain availability-* classes are safelisted in Tailwind: they were
only kept on TYPO3 10 because the ContingentViewHelper named them, and
the class names are now built in the controller.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 14:03:59 +01:00
fromme c63cbdd3a6 feat: redirects for legacy domains 2026-10-29 12:19:59 +01:00
fromme e9bda426f2 chore: update project dependencies 2026-10-29 11:28:27 +01:00
frommeandClaude Opus 5.5 63e3ed4a78 feat: the iCal contingent feed for the booking portals is back
/contingents/<hotel>.ical (page type 1710) serves the same events as on
TYPO3 10: every run of BLOCKED or ON_REQUEST days with one status is one
"Unavailable" event. External booking portals import it.

The availability comes from MyE&P contingents/availability, the
bpn-connect snapshot without the price rule of contingents/calendar
(days without a price stay free here, as they did on TYPO3 10).
TYPO3 10 read bpn-connect directly. Compared for SVS, DPW and DGS: the
events are identical; the header differs only by the iCalcreator
version (2.41 instead of 2.39) and its TZUNTIL, both RFC-conform.

When MyE&P cannot answer, the feed returns 503 with Retry-After instead
of an empty calendar, so the portals keep their last import rather than
seeing every day as free. The response is sent past the page cache,
which would drop Content-Disposition, and staticfilecache is off for the
page type.

Requires the MyE&P endpoint on my.ep-reisen.de before deploying; until
then every feed answers 503.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 11:26:14 +01:00
frommeandClaude Opus 5.5 9aabff6b5b feat: dashboard widget shows the import status
Replaces the TYPO3 10 system information entry "Letzer Produktdaten-Import":
for travel data, snow reports and groups.swiss the widget shows the data
state, the last and next scheduler run and the last error, plus MyE&P's
last export. Tasks are found by their command, not by uid. A task whose
next run is more than an hour overdue means scheduler:run is no longer
called.

New dashboards get an "Importe" tab; it can also be added via the wizard.
Editors need the Dashboard module and the widget enabled in the office
group (backend, after the deploy).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 17:34:50 +01:00
frommeandClaude Opus 5.5 863dd9de1a feat: site settings are edited in the backend, per environment
The constants of the sites become editable in Site Management >
Settings (admins only in TYPO3 13.4). settings.definitions.yaml of the
set ep/theme defines all 116 constants plus googleTagManagerId, with the
labels and categories of their constant editor annotations and today's
constants.typoscript values as defaults. Every setting is a string,
yes/no ones with a select of exactly '0' and '1': a bool setting would
hand '' instead of '0' to TypoScript.

config/sites/*/settings.yaml become shared files on the server and leave
git, like settings.php: production is leading, so backend edits survive
deploys, and each environment keeps its own values. Nine settings nobody
reads (twitterUrl, vimeoUrl, cookiefirstKey, ...) were dropped locally;
on production they stay in the seeded files until removed there.

deploy:check:site-settings aborts before anything else touches the
server if one of the shared files is missing or empty: deploy:shared
would create it empty, silently resetting that site to the defaults.

Rollout, after the site set is live: seed the shared files from the
live release once, then deploy:
  cd <deploy_path> && for f in current/config/sites/*/settings.yaml; do
    mkdir -p "shared/${f#current/}" && rmdir "shared/${f#current/}" &&
    cp -p "$f" "shared/${f#current/}"; done

Verified locally: the resolved setup of 43 pages is unchanged and only
the nine unused constants are gone (switched state); the root template
state is unchanged; all 1912 frontend URLs are identical.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 12:05:23 +01:00
frommeandClaude Opus 5.5 26a7d5f78a chore: extension icons from the E&P logo, ep_theme is stable
The icon is the E&P signet on a square canvas. ep_theme's
Resources/Public is build output, so its icon is copied there by
the asset build from Resources/Private/Assets/icons.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:46:36 +01:00
frommeandClaude Opus 5.5 8f667e92ee chore: EXT:felogin removed
Not in use: no login form in the content, and no group grants it.
Its TypoScript and the felogin_login content type are gone with it;
the TypoScript of all other keys and the frontend output are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:46:36 +01:00
frommeandClaude Opus 5.5 0ac58c1033 feat: sites get their TypoScript from the site set ep/theme
The set includes the same ep_theme TypoScript the root templates
included statically, and config/sites/*/settings.yaml hold the
constants of each site's root template, generated with TYPO3's own
TypoScript parser and kept under their old names, so no TypoScript
changes. They are plain values without definitions, so nothing is
type-converted.

Rolling out takes a second step: the root templates, with their clear
flags, still replace everything the set provides, so this deploy alone
changes nothing. .ddev/plans/sql/2026-10-27-site-sets.sql then hides
them and moves their setup (date picker presets, partner IDs, ...) into
extension templates editors keep maintaining. Extension templates
further down the tree keep applying on top.

Verified locally on a prod copy:
- the resolved constants and setup of 43 pages (every site root, every
  page with an extension template below it, every page layout) are
  identical before the deploy, between deploy and SQL, after the SQL
  and after its rollback
- a crawl of all 1912 frontend URLs is identical before and after

Rollback: always run 2026-10-27-site-sets-rollback.sql BEFORE rolling
back this code, or the sites have no TypoScript at all in between.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:46:26 +01:00
frommeandClaude Opus 5.5 903292d84c feat: products can have their own text in the subsite popup
The popup that announces the switch to another site (e.g. from a
teaser on ep-reisen.de to semesterende-skireisen.de) showed the text of
the product's concept for every product. A product now has an optional
popup text (new "Popup" tab) that replaces the concept's; empty, the
concept's text stays. Title, image and button remain the concept's, and
whether a popup appears at all is still up to the concept.

Teasers pass the product to the popup request. Pages cached before the
deploy request it without one and get the concept's text, as before.

The new column tx_epproducts_domain_model_product.popup_text is the only
pending schema change; the deploy's extension:setup adds it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:41:18 +01:00
frommeandClaude Opus 5.5 170e14dae5 fix: product pages no longer serve one visitor's back link to everyone
The "back to search results" link took ?ref= as it came and was cached
with the page, which ignores ref: the first visitor of a product page
set the link for every later one, and a javascript: URL in ref put
script into the page for all of them (verified locally).

The page now always renders the link hidden and without a target; the
backlink Stimulus controller fills in the visitor's own ?ref= and shows
it, only for URLs of the same origin. The label stays "zurück zum
Suchergebnis": refs only come from the search, and the old check
(strpos() !== -1) always picked that label anyway.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:28:11 +01:00
frommeandClaude Opus 5.5 b428ce9086 feat: ddev anonymize-db removes personal data from production dumps
Runs automatically after every `ddev import-db`. Drops all zzz_deleted_
tables (renamed in production but still holding their data, e.g. the
retired address check and form answers), replaces names and emails of
backend and frontend users, sets every password to the local dev
password "password" and clears reset tokens and MFA secrets. Sessions,
record locks, CSP reports and logs are emptied (IP addresses, user
agents). Usernames stay, so everybody logs in locally as their own user.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 10:13:02 +01:00
frommeandClaude Opus 5.5 9265e0d1c1 fix: CKEditor 5 list item ids no longer end up in the content
CKEditor 5 saves a data-list-item-id attribute on every list item, which
it only needs inside the editor; it bloated the frontend HTML and the RTE
content the API and the content feed hand to MyE&P. Both RTE presets now
import ep_theme's Processing.yaml, which removes the attribute on the way
into the database (exitHTMLparser_db); everything else passes unchanged.

Content saved since the go-live is cleaned once by hand after the
deploy (.ddev/plans/sql/2026-10-26-rte-list-item-ids.sql).

Checked locally as an editor: an RTE field with list item ids is stored
without them, other attributes (class on li, data-* on links) stay;
without the change the ids are stored.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 13:05:46 +01:00
frommeandClaude Opus 5.5 c61dddfb1d chore: travel dates are no longer translatable
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]>
2026-10-26 13:00:17 +01:00
frommeandClaude Opus 5.5 7d3b6faf6c fix: editors can create facts and lineup items again
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]>
2026-10-26 12:55:06 +01:00
frommeandClaude Opus 5.5 99cd3c1898 fix: editors can create records in the folders outside the sites again
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]>
2026-10-26 12:31:55 +01:00
frommeandClaude Opus 5.5 ecd5ea66c5 refactor: type declarations via Rector
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]>
2026-10-26 11:21:12 +01:00
frommeandClaude Opus 5.5 4fe06267c9 refactor: code quality via Rector, strict types where safe
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]>
2026-10-26 11:15:38 +01:00
frommeandClaude Opus 5.5 f4556ee75c refactor: remove dead code via Rector
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]>
2026-10-26 11:10:20 +01:00
frommeandClaude Opus 5.5 9347112d6b refactor: private instead of protected where nothing relies on it, constructor promotion
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]>
2026-10-26 11:05:45 +01:00
frommeandClaude Opus 5.5 b51fa4ddad refactor: PHP 8.5 language features via Rector
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]>
2026-10-26 10:59:53 +01:00
frommeandClaude Opus 5.5 96632edf44 fix: hotel and product images in the API no longer depend on cache order
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]>
2026-10-26 10:59:16 +01:00
frommeandClaude Opus 5.5 fe3af34063 fix: one-column teasers without an image no longer break the page
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]>
2026-10-26 10:52:11 +01:00
frommeandClaude Opus 5.5 3fa4a81e97 chore: remove the outdated copyright notices
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]>
2026-10-26 10:41:24 +01:00
frommeandClaude Opus 5.5 4a3ef91dd0 chore: fileadmin is a shared dir of the TYPO3 13 deployment
fileadmin moves from the TYPO3 10 release
(ep-reisen-typo3/shared/public/fileadmin) to
ep-reisen-typo3-13/shared/public/fileadmin, so Deployer links it like the
other shared dirs (relative symlink) and the typo3:fileadmin task with
its absolute fileadmin_path goes.

deploy:shared creates a missing shared dir empty, which would leave the
sites without any file: deploy:check:fileadmin aborts the deploy before
anything happens on the server if the shared fileadmin is missing. Deploy
this only after the directory has been moved.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 09:48:17 +01:00
frommeandClaude Opus 5.5 ff45147964 chore: remove the one-off ep:scheduler-tasks command
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]>
2026-10-26 09:44:32 +01:00
frommeandClaude Opus 5.5 386d89a1e0 chore: forms post to their own page instead of the form page type
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]>
2026-10-26 09:33:32 +01:00
frommeandClaude Opus 5.5 114a45aed3 chore: retire the snow report and search bar islands
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]>
2026-10-26 09:31:55 +01:00
259 changed files with 4181 additions and 5644 deletions

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
-- Removes personal data from a production dump. Run by `ddev anonymize-db`, which ddev also
-- runs after every `ddev import-db`. Safe to run more than once.
-- @devPasswordHash is set by the command before this file runs.
-- Tables renamed by the database analyzer in production keep their data until dropped there
SET @zzzTables = (
SELECT GROUP_CONCAT('`', table_name, '`')
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name LIKE 'zzz\_deleted\_%'
);
SET @dropZzzTables = IF(@zzzTables IS NULL, 'DO 0', CONCAT('DROP TABLE ', @zzzTables));
PREPARE dropZzzTables FROM @dropZzzTables;
EXECUTE dropZzzTables;
DEALLOCATE PREPARE dropZzzTables;
-- Usernames stay, so everybody logs in locally as their own user (with the dev password)
UPDATE be_users SET
realName = IF('' = realName, '', CONCAT('BE User ', uid)),
email = IF('' = email, '', CONCAT('be-user-', uid, '@example.invalid')),
password = @devPasswordHash,
password_reset_token = '',
mfa = NULL;
UPDATE fe_users SET
username = CONCAT('fe-user-', uid),
password = @devPasswordHash,
name = IF('' = name, '', CONCAT('FE User ', uid)),
first_name = IF('' = first_name, '', 'Vorname'),
middle_name = '',
last_name = IF('' = last_name, '', CONCAT('Nachname ', uid)),
address = IF('' = address, '', 'Musterstraße 1'),
telephone = IF('' = telephone, '', '0000 000000'),
email = IF('' = email, '', CONCAT('fe-user-', uid, '@example.invalid')),
zip = IF('' = zip, '', '00000'),
city = IF('' = city, '', 'Musterstadt');
-- Sessions, locks, reports and logs: IP addresses, user agents, user data
TRUNCATE be_sessions;
TRUNCATE fe_sessions;
TRUNCATE sys_lockedrecords;
TRUNCATE sys_http_report;
TRUNCATE sys_log;
TRUNCATE tx_epproducts_log;
TRUNCATE tx_epevents_log;
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
## Description: Remove personal data from the imported production database
## Usage: anonymize-db
## Example: "ddev anonymize-db"
set -euo pipefail
DEV_PASSWORD=password
# TYPO3 verifies argon2i hashes with plain password_verify()
hash=$(php -r 'echo password_hash($argv[1], PASSWORD_ARGON2I);' "$DEV_PASSWORD")
{
echo "SET @devPasswordHash = '${hash}';"
cat /var/www/html/.ddev/anonymize-db.sql
} | mysql --host=db --user=db --password=db db
echo "Database anonymized. Backend users log in with their username and the password '${DEV_PASSWORD}'."
+4
View File
@@ -313,3 +313,7 @@ corepack_enable: false
#hooks:
# post-start:
# - exec: composer install -d /var/www/html
hooks:
post-import-db:
- exec: .ddev/commands/web/anonymize-db
+16 -55
View File
@@ -1,65 +1,26 @@
# TYPO3 CMS Base Distribution
# E&P Reisen websites (TYPO3 13)
Get going quickly with TYPO3 CMS.
TYPO3 13 LTS for www.ep-reisen.de and its subsites (ski-boarderweek, snowzone, semesterende-skireisen,
ski-wochenenden, …). Products, hotels, regions and dates live in `packages/ep_products`, templates
and assets in `packages/ep_theme`. Dates, prices and availability come from **MyE&P** (`EP/myep`),
which in turn reads bpn-connect.
## Prerequisites
## Local setup
* PHP 8.2
* [Composer](https://getcomposer.org/download/)
DDEV project: `ddev start`, then `ddev composer install` and `ddev npm install && ddev npm run build`.
Rebuild the assets after template changes: Tailwind purges classes it does not find in the
templates.
## Quickstart
## Documentation
* `composer create-project typo3/cms-base-distribution project-name ^13`
* `cd project-name`
- [docs/cms-api.md](docs/cms-api.md): the API TYPO3 serves to MyE&P and the chatbot
(`/api/hotel`, `/api/product`, `/api/calendar-hotels`, `/api/content/*`).
- [docs/calendars.md](docs/calendars.md): price calendar, Belegungskalender and iCal feed: where
the data comes from, caching, and what to check when a calendar does not load.
Note that this distribution installs most, but not all of the TYPO3 CMS core extensions.
Depending on your need you might also want to install other TYPO3 extensions from
[packagist.org](https://packagist.org/?type=typo3-cms-framework).
## Deployment
### Setup
To start an interactive installation, you can do so by executing the following
command and then follow the wizard:
```bash
composer exec typo3 setup
```
### Setup unattended (optional)
If you're a more advanced user, you might want to leverage the unattended installation.
To do this, you need to execute the following command and substitute the arguments
with your own environment configuration.
```bash
export TYPO3_SETUP_ADMIN_PASSWORD=$(tr -dc "_A-Za-z0-9#=$()/" < /dev/urandom | head -c24)
composer exec -- typo3 setup \
--no-interaction \
--server-type=other \
--driver=sqlite \
--admin-username=admin \
--admin-email="[email protected]" \
--project-name="My TYPO3 Project" \
--create-site="http://localhost:8000/"
echo "Admin password: ${TYPO3_SETUP_ADMIN_PASSWORD}"
```
### Development server
While it's advised to use a more sophisticated web server such as
Apache 2 or Nginx, you can instantly run the project by using PHPs` built-in
[web server](https://secure.php.net/manual/en/features.commandline.webserver.php).
* `TYPO3_CONTEXT=Development php -S localhost:8000 -t public`
* open your browser at "http://localhost:8000"
Please be aware that the built-in web server is single threaded and only meant
to be used for development.
## Next steps
* [Getting Started with TYPO3](https://docs.typo3.org/permalink/t3start:start)
* [Create a Site Package](https://docs.typo3.org/permalink/t3sitepackage:start)
`deploy.php` (Deployer). A deploy runs `extension:setup`, resets OPcache and flushes all caches.
## License
+1 -1
View File
@@ -20,6 +20,7 @@
"ep/ep-theme": "@dev",
"fluidtypo3/vhs": "^8.0",
"georgringer/news": "^14.1",
"helhum/typo3-console": "^8.2",
"lochmueller/staticfilecache": "^15.2",
"ssch/typo3-encore": "^7.0",
"typo3/cms-backend": "^13.4",
@@ -29,7 +30,6 @@
"typo3/cms-dashboard": "^13.4",
"typo3/cms-extbase": "^13.4",
"typo3/cms-extensionmanager": "^13.4",
"typo3/cms-felogin": "^13.4",
"typo3/cms-filelist": "^13.4",
"typo3/cms-fluid": "^13.4",
"typo3/cms-fluid-styled-content": "^13.4",
Generated
+850 -486
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
/*/settings.yaml
+2
View File
@@ -20,3 +20,5 @@ languages:
languageId: 0
rootPageId: 2458
websiteTitle: ''
dependencies:
- ep/theme
+2
View File
@@ -20,3 +20,5 @@ languages:
languageId: 0
rootPageId: 2457
websiteTitle: ''
dependencies:
- ep/theme
+2 -4
View File
@@ -282,11 +282,7 @@ routeEnhancers:
/: 0
/json: 1701
/html: 1702
/form: 1704
/captcha: 3413
searchbar: 1706
searchbar-collapsed: 1707
/snowreport: 1705
.json: 1803
.ical: 1710
sitemap.xml: 1533906435
@@ -298,3 +294,5 @@ routes:
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nDisallow: /ajax/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
websiteTitle: ''
dependencies:
- ep/theme
+2 -2
View File
@@ -85,12 +85,12 @@ routeEnhancers:
default: ''
index: ''
map:
/form: 1704
/captcha: 3413
/snowreport: 1705
routes:
-
route: robots.txt
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
websiteTitle: 'Semsterende Skireisen'
dependencies:
- ep/theme
+2 -2
View File
@@ -66,9 +66,7 @@ routeEnhancers:
/: 0
/json: 1701
/html: 1702
/form: 1704
/captcha: 3413
/snowreport: 1705
sitemap.xml: 1533906435
yoast-snippetpreview.json: 1480321830
routes:
@@ -77,3 +75,5 @@ routes:
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
websiteTitle: ''
dependencies:
- ep/theme
+2 -1
View File
@@ -50,7 +50,6 @@ routeEnhancers:
/: 0
/json: 1701
/html: 1702
/form: 1704
/captcha: 3413
sitemap.xml: 1533906435
yoast-snippetpreview.json: 1480321830
@@ -60,3 +59,5 @@ routes:
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
websiteTitle: 'Ski-Wochenenden mit E&P Reisen und Events'
dependencies:
- ep/theme
+2 -2
View File
@@ -50,9 +50,7 @@ routeEnhancers:
/: 0
/json: 1701
/html: 1702
/form: 1704
/captcha: 3413
/snowreport: 1705
sitemap.xml: 1533906435
yoast-snippetpreview.json: 1480321830
routes:
@@ -60,3 +58,5 @@ routes:
route: robots.txt
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
dependencies:
- ep/theme
+2 -2
View File
@@ -50,9 +50,7 @@ routeEnhancers:
/: 0
/json: 1701
/html: 1702
/form: 1704
/captcha: 3413
/snowreport: 1705
sitemap.xml: 1533906435
yoast-snippetpreview.json: 1480321830
routes:
@@ -60,3 +58,5 @@ routes:
route: robots.txt
type: staticText
content: "User-agent: *\r\nDisallow: /typo3/\r\nDisallow: /typo3_src/\r\nAllow: /typo3/sysext/frontend/Resources/Public/*\r\n"
dependencies:
- ep/theme
+25 -8
View File
@@ -11,10 +11,8 @@ set('cachetool_url', 'https://github.com/gordalina/cachetool/releases/download/1
set('application', 'ep-reisen-typo3-13');
set('keep_releases', 3);
// fileadmin of the TYPO3 10 release until it is moved into shared/ (plan 4a, tidy up step 2)
set('fileadmin_path', '/usr/home/eptypo3/public_html/ep-reisen-typo3/shared/public/fileadmin');
set('shared_dirs', [
'{{typo3/public_dir}}/fileadmin',
'{{typo3/public_dir}}/typo3temp/assets',
'var/lock',
'var/log',
@@ -23,10 +21,16 @@ set('shared_dirs', [
// Baseline of the MyE&P import: without it every deploy would trigger a full re-import
'var/transient/ep_products_import',
]);
// Not in git, production is leading (encryptionKey, install tool, backend changes)
// Not in git, production is leading (encryptionKey, install tool, backend changes; the site
// settings are edited in the backend's Settings module, one settings.yaml per site)
set('site_settings_files', array_map(
static fn(string $config): string => 'config/sites/' . basename(dirname($config)) . '/settings.yaml',
glob(__DIR__ . '/config/sites/*/config.yaml')
));
set('shared_files', [
'config/system/secrets.php',
'config/system/settings.php',
...get('site_settings_files'),
]);
set('writable_dirs', [
'{{typo3/public_dir}}/typo3temp/assets',
@@ -91,9 +95,21 @@ task('deploy:build', function () {
runLocally('ddev exec bash -c "cd var/deploy-build && composer install --no-dev --prefer-dist --no-progress --no-interaction --optimize-autoloader"');
})->once();
task('typo3:fileadmin', function () {
// 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');
// deploy:shared would create a missing shared dir empty, and the sites would lose every file
task('deploy:check:fileadmin', function () {
if (!test('[ -d {{deploy_path}}/shared/{{typo3/public_dir}}/fileadmin ]')) {
throw new \RuntimeException(parse('{{deploy_path}}/shared/{{typo3/public_dir}}/fileadmin is missing'));
}
});
// The same for a site's settings.yaml: an empty one silently resets the site to the default settings
// (navigation, storage folders, ...). A new site's file is put in place by hand first.
task('deploy:check:site-settings', function () {
foreach (get('site_settings_files') as $file) {
if (!test("[ -s {{deploy_path}}/shared/$file ]")) {
throw new \RuntimeException(parse("{{deploy_path}}/shared/$file is missing or empty"));
}
}
});
// current_path instead of the recipe's release_path, so it also works after a rollback
@@ -103,6 +119,8 @@ task('typo3:cache:flush', function () {
task('deploy', [
'deploy:info',
'deploy:check:fileadmin',
'deploy:check:site-settings',
// Build locally first, so a failing build aborts before the server is touched at all.
'deploy:build',
'deploy:setup',
@@ -110,7 +128,6 @@ task('deploy', [
'deploy:release',
'rsync',
'deploy:shared',
'typo3:fileadmin',
'deploy:writable',
// The schema has to exist before the new code starts serving traffic.
'typo3:extension:setup',
+116
View File
@@ -0,0 +1,116 @@
# Availability and prices on product pages
TYPO3 shows three things built from contingent data. None of them talks to bpn-connect directly:
all go through **MyE&P**, which keeps a local snapshot of bpn-connect's availability and holds the
group house prices.
| What | Where | Shown for | MyE&P endpoint |
|---|---|---|---|
| **Price calendar** + price tables | product detail, tab "Termine/Preise" | products with a calendar hotel and season **summer** (`s`) | `contingents/calendar`, `contingents/prices` |
| **Belegungskalender** (availability calendar) | product detail, sidebar | products with a calendar hotel and season **not summer** | `contingents/availability` (+ `contingents/calendar` for the months to leave out) |
| **iCal feed** for booking portals | `https://www.ep-reisen.de/contingents/<hotel>.ical` | every hotel | `contingents/availability` |
So every product with a calendar hotel shows exactly one calendar. The MyE&P side is documented in
its `docs/api-consumer-guide.md` (`EP/myep`).
## The calendar hotel field
A product's **calendar hotel** (`calendar_hotel`) decides whose availability and prices it shows.
TYPO3 sends that hotel's **code** to MyE&P, which only understands **BusPro calendar codes** (the
codes bpn-connect knows, e.g. `DGS`, `LPJ`, `DKS`).
Most hotel records carry the BusPro code. Group houses also have **content records** of their own
(`DKSGRP`, `LPJGRP`, `SERASCSO`, …) whose code is the **CMS code** MyE&P uses to fetch content via
`/api/hotel`. These must **not** be used as calendar hotel: MyE&P and bpn-connect do not know
them, and the calendar shows its error message. This broke three example trips until 2026-10-29.
| Use as calendar hotel | Not |
|---|---|
| `DKS` Sportclub Schweizerhaus (uid 193) | `DKSGRP` Davos Klosters - Gruppenhaus Schweizerhaus |
| `LPJ` Sportclub Jenatsch (uid 37) | `LPJGRP` Lenzerheide - Gruppenhaus Jenatsch |
`/api/calendar-hotels` lists all calendar hotels in use; every code in it should be a BusPro code.
A hidden hotel record counts as no calendar hotel: the product shows no calendar and the code is
not in the list (e.g. `ASB` Sportclub Bader).
## Price calendar (summer)
- `Product/Detail.html`, section `Dates`, partial `Product/PriceCalendar`;
`AjaxCalendarController::priceCalendarAction` → `contingents/calendar` (status and price per
day), `priceTableAction` → `contingents/prices` for the current and the next year (loaded when
scrolled into view).
- Only works for hotels that are an **accommodation with prices** in MyE&P (summer group houses).
## Belegungskalender (winter)
- `Product/Detail.html`, section `AvailabilityCalendar`, loaded by htmx when it scrolls into view
(`intersect once`, so the hidden copy of the sidebar on the other breakpoint does not load);
`AjaxCalendarController::availabilityAction`, template `AjaxCalendar/Availability.html`,
month select `calendar_controller.js`.
- All months in one response; the select only switches between them. 12 months from the current
one, extended to the last month MyE&P has data for.
- **Months with prices are left out**: they are summer, covered by the price calendar. Only summer
months have prices today; that is the whole distinction. If `contingents/calendar` fails, all
months are shown (not cached).
- Colours as on TYPO3 10: green available (`OK`, and days without data), yellow on request
(`ON_REQUEST`), red not available (`BLOCKED`), past days grey; a status change from the previous
day is drawn as a diagonal. The `availability-*` classes are built in PHP, so they are safelisted
in `tailwind.config.js`; without that Tailwind purges them.
- Works for every hotel MyE&P syncs, with or without an accommodation (see below).
## iCal feed
- `IcalController::contingentsAction`, `IcalService`; route `/contingents/{hotel}.ical` on the
`ep-reisen` site (`config/sites/ep-reisen/config.yaml`, page 9, type 1710), `{hotel}` is the
hotel's path segment.
- One `Unavailable` event per run of `BLOCKED` or `ON_REQUEST` days with the same status, as on
TYPO3 10.
- Never cached. When MyE&P has no availability it answers `503` with `Retry-After: 900` instead of
an empty calendar, which the portals would read as "every day free"; they keep their last import.
## Which hotels MyE&P has availability for
MyE&P syncs its snapshot from bpn-connect every 15 minutes during the day and hourly at night, for
the calendar codes of its accommodations **plus** every code in TYPO3's `/api/calendar-hotels`
(see [cms-api.md](cms-api.md)). So a hotel offered only in winter needs nothing in MyE&P: setting
it as calendar hotel of a visible product is enough; it is synced with the next run.
`contingents/availability` answers:
- `400` for a code that is not synced (not an accommodation, not in TYPO3's list),
- `502` when the hotel's snapshot is missing or older than 6 hours (sync failing),
- `200` with one entry per known day otherwise.
`contingents/calendar` and `contingents/prices` additionally need an accommodation with prices.
MyE&P's `GET /api/contingents/status` lists every synced code with its source, prices, last sync
and last error: the first place to look when a calendar does not load.
## Caching
- The AJAX responses (price calendar, price tables, Belegungskalender) are page-cached on the AJAX
page for at most **1 hour** (`cache_period = 3600`) and never past midnight
(`cache_clearAtMidnight`, past days must turn grey). Browsers do not cache them
(`sendCacheHeaders = 0`).
- **Errors are not cached** (`disablePageCache()`): the next visitor triggers a new attempt.
- Every AJAX URL carries a cHash (required for `tx_epproducts_ajax`), so each hotel and year has its
own cache entry.
- Entries are tagged `ep_hotel_<code>`. Nothing flushes them automatically; a deploy flushes all
caches. To refresh by hand: `vendor/bin/typo3 cache:flush --group pages`, or flush the tag via
the `CacheManager` for a single hotel.
- On top of that comes MyE&P's sync interval, so a calendar is at most about 1¼ hours behind
bpn-connect.
## When a calendar does not load
The message is "Der Kalender konnte aktuell nicht geladen werden."; the iCal feed answers `503`.
| Cause | How to tell | Fix |
|---|---|---|
| Calendar hotel has a CMS code (`…GRP`, `…SO`) | code not BusPro; MyE&P `400`, bpn-connect `404` | set the hotel record with the BusPro code as calendar hotel |
| Code not synced by MyE&P | not in `contingents/status`; MyE&P `400` | check that the product and its calendar hotel are visible (then it is in `/api/calendar-hotels`) |
| Sync failing for the hotel | `contingents/status`: `available: false`, `lastError` | MyE&P / bpn-connect |
| MyE&P unreachable or its OAuth failing | all hotels fail; TYPO3 log `EP.EpProducts.MyEP.ApiClient` | MyE&P |
| Price calendar only: no accommodation or no prices | MyE&P `400` / empty | prices are maintained in MyE&P's Groups admin |
TYPO3 logs every failed MyE&P call in `var/log/typo3_*.log` (component
`EP.EpProducts.MyEP.ApiClient`, for the iCal feed also `EP.EpProducts.Service.IcalService`).
+101
View File
@@ -0,0 +1,101 @@
# CMS API
Endpoints TYPO3 serves to other systems: **MyE&P** (hotel and product content, calendar hotels)
and the **chatbot** (content feed). They are answered by `EP\EpProducts\Middleware\ApiMiddleware`
before the page resolver, so they do not depend on a page and are never page-cached.
The other side of these contracts is documented in MyE&P's `docs/api-consumer-guide.md`
(`EP/myep`).
## Authentication
All endpoints need the shared key `cmsApiKey` from the `ep_products` extension configuration
(Settings → Extension Configuration → ep_products, tab ContentFeed):
- header `X-Api-Key: <key>` (preferred, query strings end up in access logs), or
- query parameter `?key=<key>`.
A missing or wrong key answers `403 {"message": "Invalid or missing api key"}`. An empty
`cmsApiKey` denies every request. MyE&P sends the key as `APP_CMS_API_KEY`; both sides must be
changed together.
## `GET /api/hotel?hotel=<code>`
Content of one hotel, for MyE&P's group house pages and booking flow.
- `hotel`: the TYPO3 hotel code or one of its matchcode aliases. In MyE&P this is the
accommodation's **CMS code** (e.g. `DGSGRP`), which can differ from its calendar code.
- `400` without `hotel`, `404 {"message": "Hotel not found"}` for an unknown code.
```json
{
"name": "…", "code": "…",
"description": "…", "features": "…", "room_types": "…",
"additional_information": "…", "address": "…",
"images": {"original": [{"url": "…", "alt": "…", "title": "…"}], "resized": […]},
"icons": […],
"region": {"name": "…", "latitude": …, "longitude": …, "webcam": "…", "ski_area": "…",
"ski_area_extended": "…", "description": "…", "news": "…", "length": …,
"altitude": …, "lifts": …, "images": {…}, "region_maps": [{"url": "…", "alt": "…", "title": "…"}]}
}
```
## `GET /api/product?product=<code>[&hotel=<code>]`
Content of one product, optionally with a hotel.
- `product`: the product code. `400` without it, `404 {"message": "Product not found"}` for an
unknown code.
- `hotel` (optional): adds `hotel` with the same content as `/api/hotel`.
```json
{
"product": {"name": "…", "code": "…", "images": {…}},
"country": {"name": "…"},
"region": {…},
"city": {"name": "…"},
"hotel": {…}
}
```
## `GET /api/calendar-hotels`
The calendar hotel of every visible product: the hotel codes whose availability TYPO3 shows
(iCal feed, availability calendar, price calendar). MyE&P syncs its contingent snapshot for
these codes in addition to its accommodations, so hotels offered only in winter, which have no
accommodation in MyE&P, have availability too. See [calendars.md](calendars.md).
- Only products and hotels that are not hidden or deleted; hotels without a code are left out.
- Sorted by code; products by uid. Names have line breaks collapsed.
- `season`: `"s"` (summer), `"w"` (winter) or `null`.
```json
[
{"code": "DKI", "name": "Sportclub Schwendi", "products": [
{"uid": 155, "name": "Sportclub Schwendi", "season": "w"},
{"uid": 362, "name": "Gruppenhaus Schwendi", "season": "s"}
]}
]
```
Every code in this list should be a **BusPro calendar code**. A group house's content record
(`…GRP`, `…SO`) set as a product's calendar hotel puts its CMS code in here, which bpn-connect does
not know (see [calendars.md](calendars.md#the-calendar-hotel-field)).
## `GET /api/content/manifest` and `GET /api/content/records?type=<type>&uids=<uids>`
The content feed for the chatbot (`EP\EpProducts\ContentFeed\ContentFeedApi`).
- `manifest`: everything the feed publishes (pages of the configured sites and records), with a
modification timestamp per item. Cached for 10 minutes.
- `records`: up to 100 records of one type per request. `type` is one of `product`, `hotel`,
`region`, `country`, `city`, `concept`, `news`; `uids` comma-separated. Uids that are no longer
eligible are left out, not answered with an error (the consumer reads them as deleted).
- `400` for an unknown type, no uids or more than 100 uids; `404` for other paths.
- `503` with `Retry-After: 3600` when the feed is switched off (`contentFeedEnabled = 0`, the kill
switch for when the host is struggling), `503` when `contentFeedSites` is empty. The consumer
keeps what it has on a 503; nothing is deleted on the chatbot side.
Configuration (extension configuration `ep_products`, tab ContentFeed): `contentFeedEnabled`,
`contentFeedSites`, `contentFeedLanguages`, `contentFeedExcludedPages`,
`contentFeedNewsDetailPages`.
@@ -1,32 +1,10 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Command;
/***************************************************************
*
* 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\Service\ProductImport;
use EP\EpProducts\Service\DateImportService;
use EP\EpProducts\Service\ProductImportSource;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -38,41 +16,22 @@ use Symfony\Component\Console\Output\OutputInterface;
class DateCommandController extends Command
{
/**
* @var DateImportService
*/
protected $dateImportService;
/**
* @var ProductImportSource
*/
protected $importSource;
/**
* @param DateImportService $importService
* @param ProductImportSource $importSource
*/
public function __construct
(
DateImportService $importService,
ProductImportSource $importSource
private readonly DateImportService $dateImportService,
private readonly ProductImportSource $importSource
)
{
$this->dateImportService = $importService;
$this->importSource = $importSource;
parent::__construct();
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @throws \Doctrine\DBAL\DBALException
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$import = $this->importSource->acquire();
if ($import === null) {
if (!$import instanceof ProductImport) {
$output->writeln('No new import available.');
return 0;
}
@@ -1,82 +0,0 @@
<?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;
}
}
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\ContentFeed;
use EP\EpProducts\Http\ApiKeyAuthenticator;
@@ -19,53 +21,29 @@ class ContentFeedApi
{
public const PATH_PREFIX = '/api/content/';
private const MANIFEST_CACHE_IDENTIFIER = 'content_feed_manifest';
private const string MANIFEST_CACHE_IDENTIFIER = 'content_feed_manifest';
// The consumer polls every 15 minutes, so this caps the build at once per poll however
// often it retries. Edits reach the chatbot within about 25 minutes.
private const MANIFEST_CACHE_LIFETIME = 600;
private const int MANIFEST_CACHE_LIFETIME = 600;
private const MAX_RECORDS_PER_REQUEST = 100;
private const int MAX_RECORDS_PER_REQUEST = 100;
private const DISABLED_RETRY_AFTER = 3600;
private const int DISABLED_RETRY_AFTER = 3600;
private const JSON_FLAGS = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
/**
* @var ApiKeyAuthenticator
*/
private $authenticator;
/**
* @var ContentFeedConfiguration
*/
private $configuration;
/**
* @var ManifestBuilder
*/
private $manifestBuilder;
/**
* @var RecordSerializer
*/
private $recordSerializer;
/**
* @var FrontendInterface
*/
private $cache;
public function __construct(
ApiKeyAuthenticator $authenticator,
ContentFeedConfiguration $configuration,
ManifestBuilder $manifestBuilder,
RecordSerializer $recordSerializer,
private readonly ApiKeyAuthenticator $authenticator,
private readonly ContentFeedConfiguration $configuration,
private readonly ManifestBuilder $manifestBuilder,
private readonly RecordSerializer $recordSerializer,
CacheManager $cacheManager
) {
$this->authenticator = $authenticator;
$this->configuration = $configuration;
$this->manifestBuilder = $manifestBuilder;
$this->recordSerializer = $recordSerializer;
$this->cache = $cacheManager->getCache('ep_products_cache');
}
@@ -112,9 +90,7 @@ class ContentFeedApi
$uids = array_values(array_unique(array_filter(
GeneralUtility::intExplode(',', (string) ($request->getQueryParams()['uids'] ?? ''), true),
function (int $uid): bool {
return $uid > 0;
}
fn(int $uid): bool => $uid > 0
)));
if ([] === $uids) {
@@ -14,10 +14,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
class ContentFeedConfiguration
{
/**
* @var array
*/
private $configuration;
private array $configuration;
public function __construct(ExtensionConfiguration $extensionConfiguration)
{
@@ -16,13 +16,13 @@ class ManifestBuilder
{
public const CONTENT_PAGE_TYPE = 1730;
private const DOKTYPE_DEFAULT = 1;
private const int DOKTYPE_DEFAULT = 1;
// Recycler and backend user section: nothing below them is public.
private const DOKTYPES_NOT_TRAVERSED = [6, 255];
private const array DOKTYPES_NOT_TRAVERSED = [6, 255];
// l18n_cfg bit 1: "hide default translation of page".
private const L18N_HIDE_DEFAULT_LANGUAGE = 1;
private const int L18N_HIDE_DEFAULT_LANGUAGE = 1;
private const RECORD_FIELDS = [
private const array RECORD_FIELDS = [
'product' => ['uid', 'tstamp', 'detail_page'],
'hotel' => ['uid', 'tstamp', 'detail_page'],
'region' => ['uid', 'tstamp', 'detail_page'],
@@ -32,43 +32,13 @@ class ManifestBuilder
'news' => ['uid', 'tstamp', 'pid'],
];
/**
* @var PageTree
*/
private $pageTree;
/**
* @var UrlResolver
*/
private $urlResolver;
/**
* @var RecordFinder
*/
private $recordFinder;
/**
* @var ContentFeedConfiguration
*/
private $configuration;
/**
* @var ConnectionPool
*/
private $connectionPool;
public function __construct(
PageTree $pageTree,
UrlResolver $urlResolver,
RecordFinder $recordFinder,
ContentFeedConfiguration $configuration,
ConnectionPool $connectionPool
private readonly PageTree $pageTree,
private readonly UrlResolver $urlResolver,
private readonly RecordFinder $recordFinder,
private readonly ContentFeedConfiguration $configuration,
private readonly ConnectionPool $connectionPool,
) {
$this->pageTree = $pageTree;
$this->urlResolver = $urlResolver;
$this->recordFinder = $recordFinder;
$this->configuration = $configuration;
$this->connectionPool = $connectionPool;
}
public function build(): array
@@ -78,7 +48,7 @@ class ManifestBuilder
return [
'generatedAt' => date('c'),
'sites' => array_map([$this, 'describeSite'], $sites),
'sites' => array_map($this->describeSite(...), $sites),
'items' => array_merge($this->buildPageItems($sites, $this->collectDetailPages()), $records),
];
}
@@ -211,8 +181,14 @@ class ManifestBuilder
return 0 !== $languageId || 0 === ((int) $page['l18n_cfg'] & self::L18N_HIDE_DEFAULT_LANGUAGE);
}
private function buildPageItem(array $page, array $row, int $languageId, string $isoCode, array $breadcrumb, array $context): ?array
{
private function buildPageItem(
array $page,
array $row,
int $languageId,
string $isoCode,
array $breadcrumb,
array $context,
): ?array {
$uid = (int) $page['uid'];
// "Show content from page": the elements live on another page, and so do their changes.
// The site answers 404 when that page is hidden.
@@ -278,7 +254,7 @@ class ManifestBuilder
'pid',
'sys_language_uid',
'MAX(tstamp) AS tstamp',
"SUM(CASE WHEN deleted = 0 AND hidden = 0 AND CType <> 'list' THEN 1 ELSE 0 END) AS text_elements"
"SUM(CASE WHEN deleted = 0 AND hidden = 0 AND CType <> 'list' AND CType NOT LIKE 'epproducts\\_%' THEN 1 ELSE 0 END) AS text_elements"
)
->from('tt_content')->groupBy('pid', 'sys_language_uid')->executeQuery()
->fetchAllAssociative()
@@ -15,21 +15,13 @@ use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
*/
class PageTree
{
private const FIELDS = [
private const array FIELDS = [
'uid', 'pid', 'sorting', 'doktype', 'hidden', 'starttime', 'endtime', 'fe_group',
'extendToSubpages', 'no_index', 'title', 'nav_title', 'description', 'shortcut',
'shortcut_mode', 'content_from_pid', 'tstamp', 'sys_language_uid', 'l10n_parent', 'l18n_cfg',
];
/**
* @var ConnectionPool
*/
private $connectionPool;
/**
* @var int
*/
private $now;
private readonly int $now;
/**
* @var array<int, array>|null default-language rows by uid
@@ -39,16 +31,15 @@ class PageTree
/**
* @var array<int, int[]> child uids by parent uid, in sorting order
*/
private $children = [];
private array $children = [];
/**
* @var array<int, array<int, array>> overlay rows by default-language uid and language id
*/
private $overlays = [];
private array $overlays = [];
public function __construct(ConnectionPool $connectionPool, Context $context)
public function __construct(private readonly ConnectionPool $connectionPool, Context $context)
{
$this->connectionPool = $connectionPool;
$this->now = (int) $context->getPropertyFromAspect('date', 'timestamp');
}
@@ -26,14 +26,8 @@ class RecordFinder
'news' => 'tx_news_domain_model_news',
];
/**
* @var ConnectionPool
*/
private $connectionPool;
public function __construct(ConnectionPool $connectionPool)
public function __construct(private readonly ConnectionPool $connectionPool)
{
$this->connectionPool = $connectionPool;
}
/**
@@ -18,36 +18,12 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
*/
class RecordSerializer
{
/**
* @var RecordFinder
*/
private $recordFinder;
/**
* @var UrlResolver
*/
private $urlResolver;
/**
* @var RteLinkResolver
*/
private $rteLinkResolver;
/**
* @var ConnectionPool
*/
private $connectionPool;
public function __construct(
RecordFinder $recordFinder,
UrlResolver $urlResolver,
RteLinkResolver $rteLinkResolver,
ConnectionPool $connectionPool
private readonly RecordFinder $recordFinder,
private readonly UrlResolver $urlResolver,
private readonly RteLinkResolver $rteLinkResolver,
private readonly ConnectionPool $connectionPool,
) {
$this->recordFinder = $recordFinder;
$this->urlResolver = $urlResolver;
$this->rteLinkResolver = $rteLinkResolver;
$this->connectionPool = $connectionPool;
}
/**
@@ -62,18 +38,12 @@ class RecordSerializer
return [];
}
switch ($type) {
case 'product':
return $this->serializeProducts($rows);
case 'hotel':
return $this->serializeHotels($rows);
case 'news':
return $this->serializeNews($rows);
default:
return array_values(array_map(function (array $row) use ($type): array {
return $this->serializeDestination($type, $row);
}, $rows));
}
return match ($type) {
'product' => $this->serializeProducts($rows),
'hotel' => $this->serializeHotels($rows),
'news' => $this->serializeNews($rows),
default => array_values(array_map(fn(array $row): array => $this->serializeDestination($type, $row), $rows)),
};
}
private function serializeProducts(array $rows): array
@@ -137,7 +107,7 @@ class RecordSerializer
foreach ($rows as $uid => $row) {
$code = trim((string) $row['code']);
$codes = array_values(array_unique(array_filter(array_merge([$code], $aliases[$uid] ?? []), 'strlen')));
$codes = array_values(array_unique(array_filter(array_merge([$code], $aliases[$uid] ?? []), strlen(...))));
$items[] = $this->withLink($row, max((int) $row['tstamp'], $matchcodeTimestamps[$uid] ?? 0)) + [
'code' => '' === $code ? null : $code,
@@ -152,9 +122,7 @@ class RecordSerializer
'countryUid' => (int) $row['country'] ?: null,
'regionUid' => (int) $row['region'] ?: null,
'cityUid' => (int) $row['city'] ?: null,
'productUids' => array_values(array_filter($products[$uid] ?? [], function (int $productUid) use ($eligibleProducts): bool {
return isset($eligibleProducts[$productUid]);
})),
'productUids' => array_values(array_filter($products[$uid] ?? [], fn(int $productUid): bool => isset($eligibleProducts[$productUid]))),
'address' => $this->text($row['address']),
'latitude' => $this->text($row['latitude']),
'longitude' => $this->text($row['longitude']),
@@ -192,64 +160,61 @@ class RecordSerializer
'headline' => $this->text($row['headline']),
];
switch ($type) {
case 'region':
return $item + [
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'countryUid' => (int) $row['country'] ?: null,
'facts' => [
'length' => $this->text($row['length']),
'height' => $this->text($row['height']),
'lifts' => $this->text($row['lifts']),
],
'webcam' => $this->findWebcamUrl((int) $row['webcam']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'feature' => $this->text($row['feature']),
'skiArea' => $this->html($row['ski_area']),
'skiAreaExtended' => $this->html($row['ski_area_extended']),
'skiPass' => $this->html($row['ski_pass']),
'news' => $this->html($row['news']),
],
];
case 'country':
return $item + [
'code' => $this->text($row['code']),
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
],
];
case 'city':
return $item + [
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'countryUid' => (int) $row['country'] ?: null,
'regionUid' => (int) $row['region'] ?: null,
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
'party' => $this->html($row['party']),
'leisure' => $this->html($row['leisure']),
],
];
default: // concept
return $item + [
'code' => $this->text($row['code']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
'popupText' => $this->html($row['popup_text']),
],
];
}
return match ($type) {
'region' => $item + [
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'countryUid' => (int) $row['country'] ?: null,
'facts' => [
'length' => $this->text($row['length']),
'height' => $this->text($row['height']),
'lifts' => $this->text($row['lifts']),
],
'webcam' => $this->findWebcamUrl((int) $row['webcam']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'feature' => $this->text($row['feature']),
'skiArea' => $this->html($row['ski_area']),
'skiAreaExtended' => $this->html($row['ski_area_extended']),
'skiPass' => $this->html($row['ski_pass']),
'news' => $this->html($row['news']),
],
],
'country' => $item + [
'code' => $this->text($row['code']),
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
],
],
'city' => $item + [
'season' => $this->text($row['season']),
'keywords' => $this->text($row['keywords']),
'countryUid' => (int) $row['country'] ?: null,
'regionUid' => (int) $row['region'] ?: null,
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
'party' => $this->html($row['party']),
'leisure' => $this->html($row['leisure']),
],
],
// concept
default => $item + [
'code' => $this->text($row['code']),
'text' => [
'teaser' => $this->html($row['teaser']),
'description' => $this->html($row['description']),
'descriptionExtended' => $this->html($row['description_extended']),
'popupText' => $this->html($row['popup_text']),
],
],
};
}
private function serializeNews(array $rows): array
@@ -302,8 +267,13 @@ class RecordSerializer
* @param int[] $uids
* @return array<int, int[]> related uids by $keyField, in $sortField order
*/
private function findMmUids(string $table, string $keyField, string $valueField, array $uids, string $sortField = 'sorting'): array
{
private function findMmUids(
string $table,
string $keyField,
string $valueField,
array $uids,
string $sortField = 'sorting',
): array {
$queryBuilder = $this->connectionPool->getQueryBuilderForTable($table);
$rows = $queryBuilder
->select($keyField, $valueField)
@@ -381,12 +351,11 @@ class RecordSerializer
}
/**
* @param array $infoboxUids
* @return array<int, string|null>
*/
private function findInfoboxTexts(array $infoboxUids): array
{
$infoboxUids = array_values(array_filter(array_map('intval', $infoboxUids)));
$infoboxUids = array_values(array_filter(array_map(intval(...), $infoboxUids)));
if ([] === $infoboxUids) {
return [];
@@ -15,25 +15,13 @@ use TYPO3\CMS\Core\Resource\File;
*/
class RteLinkResolver
{
/**
* @var LinkService
*/
private $linkService;
/**
* @var UrlResolver
*/
private $urlResolver;
public function __construct(LinkService $linkService, UrlResolver $urlResolver)
public function __construct(private readonly LinkService $linkService, private readonly UrlResolver $urlResolver)
{
$this->linkService = $linkService;
$this->urlResolver = $urlResolver;
}
public function resolve(string $html): string
{
if (false === strpos($html, 't3://')) {
if (!str_contains($html, 't3://')) {
return $html;
}
@@ -52,7 +40,7 @@ class RteLinkResolver
{
try {
$data = $this->linkService->resolve($link);
} catch (\Throwable $e) {
} catch (\Throwable) {
return null;
}
@@ -13,42 +13,33 @@ use TYPO3\CMS\Core\Utility\MathUtility;
*/
class UrlResolver
{
private const DOKTYPE_DEFAULT = 1;
private const DOKTYPE_SHORTCUT = 4;
private const SHORTCUT_MODE_NONE = 0;
private const SHORTCUT_MODE_FIRST_SUBPAGE = 1;
private const SHORTCUT_MODE_PARENT_PAGE = 3;
private const MAX_SHORTCUT_DEPTH = 5;
/**
* @var SiteFinder
*/
private $siteFinder;
/**
* @var PageTree
*/
private $pageTree;
private const int DOKTYPE_DEFAULT = 1;
private const int DOKTYPE_SHORTCUT = 4;
private const int SHORTCUT_MODE_NONE = 0;
private const int SHORTCUT_MODE_FIRST_SUBPAGE = 1;
private const int SHORTCUT_MODE_PARENT_PAGE = 3;
private const int MAX_SHORTCUT_DEPTH = 5;
/**
* @var string[]
*/
private $includedSites;
private readonly array $includedSites;
/**
* @var array<string, int> news detail page uid by site identifier
*/
private $newsDetailPages;
private array $newsDetailPages;
/**
* @var array<int, Site|null>
*/
private $sitesByPage = [];
private array $sitesByPage = [];
public function __construct(SiteFinder $siteFinder, PageTree $pageTree, ContentFeedConfiguration $configuration)
{
$this->siteFinder = $siteFinder;
$this->pageTree = $pageTree;
public function __construct(
private readonly SiteFinder $siteFinder,
private readonly PageTree $pageTree,
ContentFeedConfiguration $configuration,
) {
$this->includedSites = $configuration->getSites();
$this->newsDetailPages = $configuration->getNewsDetailPages();
}
@@ -63,7 +54,7 @@ class UrlResolver
foreach ($this->includedSites as $identifier) {
try {
$sites[] = $this->siteFinder->getSiteByIdentifier($identifier);
} catch (SiteNotFoundException $e) {
} catch (SiteNotFoundException) {
// A misspelt identifier must not take the whole feed down; it just contributes nothing.
}
}
@@ -80,7 +71,7 @@ class UrlResolver
try {
$site = $this->siteFinder->getSiteByPageId($pageUid);
$this->sitesByPage[$pageUid] = in_array($site->getIdentifier(), $this->includedSites, true) ? $site : null;
} catch (SiteNotFoundException $e) {
} catch (SiteNotFoundException) {
$this->sitesByPage[$pageUid] = null;
}
}
@@ -92,7 +83,7 @@ class UrlResolver
{
$site = $this->getIncludedSite($pageUid);
if (null === $site) {
if (!$site instanceof Site) {
return null;
}
@@ -100,7 +91,7 @@ class UrlResolver
$language = $site->getLanguageById($languageId);
return (string) $site->getRouter()->generateUri($pageUid, ['_language' => $language] + $parameters);
} catch (\Throwable $e) {
} catch (\Throwable) {
// Unroutable pages (missing slug, disabled language) are left out rather than failing the feed.
return null;
}
@@ -161,7 +152,7 @@ class UrlResolver
$pageUid = $this->parsePageReference($detailPage);
$target = null === $pageUid ? null : $this->resolveLinkTarget($pageUid);
$site = null === $target ? null : $this->getIncludedSite($target);
$url = null === $site ? null : $this->getPageUrl($target);
$url = $site instanceof Site ? $this->getPageUrl($target) : null;
return [
'url' => $url,
@@ -179,7 +170,7 @@ class UrlResolver
{
$site = $this->getIncludedSite($storagePid);
if (null === $site) {
if (!$site instanceof Site) {
return null;
}
@@ -2,31 +2,7 @@
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 League\Period\Period;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\MyEP\ApiClient as MyEpClient;
use EP\EpProducts\MyEP\ApiException;
@@ -37,11 +13,8 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxCalendarController extends ActionController
{
protected MyEpClient $myEpClient;
public function __construct(MyEpClient $myEpClient)
public function __construct(private readonly MyEpClient $myEpClient)
{
$this->myEpClient = $myEpClient;
}
public function priceCalendarAction(Hotel $hotel, int $months = 12): ResponseInterface
@@ -116,7 +89,7 @@ class AjaxCalendarController extends ActionController
if (null !== $lastDataDate) {
$dataEnd = $lastDataDate->modify('first day of next month')->setTime(0, 0, 0);
if ($dataEnd > $period->getEndDate()) {
$period = new \League\Period\Period($period->getStartDate(), $dataEnd);
$period = new Period($period->getStartDate(), $dataEnd);
}
}
} catch (ApiException $e) {
@@ -139,9 +112,100 @@ class AjaxCalendarController extends ActionController
return $this->htmlResponse();
}
/**
* Availability calendar (Belegungskalender) of the product detail sidebar, for products
* without the price calendar, limited to the months without prices (winter). TYPO3 10
* fetched it month by month from bpn-connect; MyE&P returns the whole snapshot at once,
* so every month is rendered and the month select only switches between them.
*/
public function availabilityAction(Hotel $hotel, int $months = 12): ResponseInterface
{
$hotelCode = trim($hotel->getCode() ?? '');
$this->tagPageCacheForHotel($hotelCode);
$period = DateUtility::getDateRange(null, null, $months)['period'];
$today = new \DateTimeImmutable('today');
$statusByDate = [];
$error = false;
if ('' === $hotelCode) {
$error = true;
$this->disablePageCache();
} else {
try {
foreach ($this->myEpClient->getContingentAvailability($hotelCode) as $item) {
if (!is_array($item) || !isset($item['date'])) {
continue;
}
$statusByDate[$item['date']] = $this->normalizeAvailabilityStatus((string)($item['status'] ?? 'OK'));
}
} catch (ApiException $e) {
$error = true;
$this->disablePageCache();
}
}
if ([] !== $statusByDate) {
$lastDataDate = new \DateTimeImmutable(max(array_keys($statusByDate)));
$dataEnd = $lastDataDate->modify('first day of next month')->setTime(0, 0, 0);
if ($dataEnd > $period->getEndDate()) {
$period = new Period($period->getStartDate(), $dataEnd);
}
}
// Days without a row are available, as on TYPO3 10. A change of the occupancy level
// from the previous day is drawn as a diagonal transition (arrival/departure day).
$classByDate = [];
$previousStatus = $statusByDate[$period->getStartDate()->modify('-1 day')->format('Y-m-d')] ?? 'OK';
for ($day = \DateTimeImmutable::createFromInterface($period->getStartDate()); $day < $period->getEndDate(); $day = $day->modify('+1 day')) {
$dateKey = $day->format('Y-m-d');
$status = $statusByDate[$dateKey] ?? 'OK';
if ($day < $today) {
$classByDate[$dateKey] = 'text-zinc-400';
} elseif ($previousStatus !== $status) {
$classByDate[$dateKey] = 'text-white availability-' . $this->getAvailabilitySuffix($previousStatus) . '-to-' . $this->getAvailabilitySuffix($status);
} else {
$classByDate[$dateKey] = 'text-white availability-' . $this->getAvailabilitySuffix($status);
}
$previousStatus = $status;
}
// Only summer months have prices in the price config, and those are covered by the
// price calendar, so they are left out here. If the price config is unavailable, all
// months are shown, but not cached.
$priceMonths = [];
if (!$error) {
try {
foreach ($this->myEpClient->getPriceConfigData($hotelCode) as $item) {
if (!is_array($item) || !isset($item['date'])) {
continue;
}
if (null !== ($item['pricePerNight'] ?? null) || null !== ($item['defaultPricePerNight'] ?? null)) {
$priceMonths[substr((string)$item['date'], 0, 7)] = true;
}
}
} catch (ApiException $e) {
$this->disablePageCache();
}
}
$calendarMonths = array_values(array_filter(
$this->buildCalendarMonths($period),
fn (array $month): bool => !isset($priceMonths[$month['date']->format('Y-m')])
));
$this->view->assignMultiple([
'classByDate' => $classByDate,
'calendarMonths' => $calendarMonths,
'error' => $error,
]);
return $this->htmlResponse();
}
public function priceTableAction(Hotel $hotel, ?string $year = null): ResponseInterface
{
$year = $year ?? (new \DateTimeImmutable())->format('Y');
$year ??= new \DateTimeImmutable()->format('Y');
$hotelCode = trim($hotel->getCode() ?? '');
$this->tagPageCacheForHotel($hotelCode);
@@ -194,6 +258,25 @@ class AjaxCalendarController extends ActionController
$this->request->getAttribute('frontend.cache.instruction')?->disableCache('Price calendar data could not be retrieved');
}
private function normalizeAvailabilityStatus(string $status): string
{
$status = strtoupper(trim($status));
if ('ONREQUEST' === $status) {
return 'ON_REQUEST';
}
return in_array($status, ['OK', 'ON_REQUEST', 'BLOCKED'], true) ? $status : 'OK';
}
private function getAvailabilitySuffix(string $status): string
{
return match ($status) {
'BLOCKED' => 'none',
'ON_REQUEST' => 'partial',
default => 'full',
};
}
private function buildTooltip(string $status, array $item): string
{
if ($status === 'blocked') {
@@ -237,7 +320,7 @@ class AjaxCalendarController extends ActionController
/**
* @return array<int, array<string, mixed>>
*/
private function buildCalendarMonths(\League\Period\Period $period): array
private function buildCalendarMonths(Period $period): array
{
$months = [];
$current = new \DateTimeImmutable($period->getStartDate()->format('Y-m-01 00:00:00'));
@@ -2,30 +2,6 @@
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2021 Björn Fromme <[email protected]>, dreipunktnull
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Service\FilterService;
use EP\EpProducts\Service\SearchResultService;
@@ -38,23 +14,8 @@ class AjaxFilterPanelController extends ActionController
use RequestArgumentTypeConversionTrait;
/**
* @var FilterService
*/
protected $filterService;
/**
* @var SearchResultService
*/
private $searchResultService;
/**
* @param FilterService $filterService
*/
public function __construct(FilterService $filterService, SearchResultService $searchResultService)
public function __construct(private FilterService $filterService, private SearchResultService $searchResultService)
{
$this->filterService = $filterService;
$this->searchResultService = $searchResultService;
}
public function initializeAction(): void
@@ -63,9 +24,6 @@ class AjaxFilterPanelController extends ActionController
$this->processFilterSettingsArgument($forcedConceptUids);
}
/**
* @param array $filterSettings
*/
public function indexAction(array $filterSettings): ResponseInterface
{
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'] ?? '');
@@ -2,30 +2,6 @@
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\Service\FilterService;
use EP\EpProducts\Service\SearchResultService;
@@ -39,35 +15,16 @@ class AjaxSearchController extends ActionController
use RequestArgumentTypeConversionTrait;
/**
* @var FilterService
*/
protected $filterService;
/**
* @var SearchResultService
*/
protected $searchResultService;
/**
* @param FilterService $filterService
* @param SearchResultService $searchResultService
*/
public function __construct(FilterService $filterService, SearchResultService $searchResultService)
public function __construct(private FilterService $filterService, private SearchResultService $searchResultService)
{
$this->filterService = $filterService;
$this->searchResultService = $searchResultService;
}
public function initializeSearchresultAction()
public function initializeSearchresultAction(): void
{
$forcedConceptUids = GeneralUtility::intExplode(',', $this->settings['forcedConceptUids'] ?? '', true);
$this->processFilterSettingsArgument($forcedConceptUids);
}
/**
* @param array $filterSettings
*/
public function searchresultAction(array $filterSettings): ResponseInterface
{
$dateSelected = false;
@@ -2,30 +2,6 @@
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\Http\ForwardResponse;
use EP\EpProducts\Domain\Model\Infobox;
@@ -48,61 +24,17 @@ class AjaxTableController extends ActionController
use RequestArgumentTypeConversionTrait;
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var InfoboxRepository
*/
protected $infoboxRepository;
/**
* @var FilterService
*/
protected $filterService;
/**
* @var DateService
*/
protected $dateService;
/**
* @var ResellerDataService
*/
protected $resellerDataService;
/**
* @var MyEpUrlResolverService
*/
protected $myEpUrlResolverService;
/**
* @param ProductRepository $productRepository
* @param InfoboxRepository $infoboxRepository
* @param DateService $dateService
* @param FilterService $filterService
* @param ResellerDataService $resellerDataService
* @param MyEpUrlResolverService $myEpUrlResolverService
*/
public function __construct(
ProductRepository $productRepository,
InfoboxRepository $infoboxRepository,
DateService $dateService,
FilterService $filterService,
ResellerDataService $resellerDataService,
MyEpUrlResolverService $myEpUrlResolverService
private ProductRepository $productRepository,
private InfoboxRepository $infoboxRepository,
private DateService $dateService,
private FilterService $filterService,
private ResellerDataService $resellerDataService,
private MyEpUrlResolverService $myEpUrlResolverService,
) {
$this->productRepository = $productRepository;
$this->infoboxRepository = $infoboxRepository;
$this->dateService = $dateService;
$this->filterService = $filterService;
$this->resellerDataService = $resellerDataService;
$this->myEpUrlResolverService = $myEpUrlResolverService;
}
public function initializeDatesAction()
public function initializeDatesAction(): void
{
if ($this->request->hasArgument('dateFrom')) {
$dateFrom = $this->request->getArgument('dateFrom');
@@ -114,12 +46,6 @@ class AjaxTableController extends ActionController
}
}
/**
* @param Product $product
* @param Hotel $hotel
* @param string|null $dateFrom
* @param string|null $dateTo
*/
public function datestableAction(
Product $product,
Hotel $hotel,
@@ -141,7 +67,7 @@ class AjaxTableController extends ActionController
if (1 === count($dates)) {
$date = reset($dates);
return (new ForwardResponse('pricetable'))->withExtensionName('ep_products')->withArguments([
return new ForwardResponse('pricetable')->withExtensionName('ep_products')->withArguments([
'product' => $product,
'hotel' => $hotel,
'date' => $date['dateUid'],
@@ -160,9 +86,7 @@ class AjaxTableController extends ActionController
'nonBookableDateUids' => $nonBookableDateUids,
]);
$bookableDates = array_filter($allDates, function (array $date) {
return $date['available'] > 0;
});
$bookableDates = array_filter($allDates, fn(array $date): bool => $date['available'] > 0);
$includedServices = $this->dateService->groupIncludedServices($bookableDates);
$altProductLink = null;
@@ -211,12 +135,6 @@ class AjaxTableController extends ActionController
return $this->htmlResponse();
}
/**
* @param Product $product
* @param Hotel $hotel
* @param Date $date
* @param bool $singleDate
*/
public function pricetableAction(
Product $product,
Hotel $hotel,
@@ -265,10 +183,6 @@ class AjaxTableController extends ActionController
return $this->htmlResponse();
}
/**
* @param Product $product
* @param Hotel $hotel
*/
public function pricetableHtmlAction(Product $product, Hotel $hotel): ResponseInterface
{
$nonBookableInfo = $this->getNonBookableInfobox($product);
@@ -293,18 +207,13 @@ class AjaxTableController extends ActionController
return $this->htmlResponse();
}
/**
* @param Product $product
*/
public function eventPricetableAction(Product $product): ResponseInterface
{
$roomTypes = GeneralUtility::trimExplode(',', $this->settings['priceTableRoomTypes'] ?? '');
$roomTypesPax = array_map(function ($type) {
return (int) $type;
}, $roomTypes);
$roomTypesLabels = array_map(function ($type) {
$roomTypesPax = array_map(fn($type): int => (int) $type, $roomTypes);
$roomTypesLabels = array_map(function ($type): string {
$label = (int) $type . 'er';
if (false !== strpos($type, '+')) {
if (str_contains($type, '+')) {
$label .= '+';
}
return $label;
@@ -334,10 +243,9 @@ class AjaxTableController extends ActionController
}
/**
* @param Product $product
* @return Infobox
*/
protected function getNonBookableInfobox(Product $product)
private function getNonBookableInfobox(Product $product)
{
if (null !== $nonBookableInfo = $product->getNonBookableInfo()) {
return $nonBookableInfo;
@@ -4,14 +4,21 @@ namespace EP\EpProducts\Controller;
use Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Concept;
use EP\EpProducts\Domain\Model\Product;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxTeaserPopupController extends ActionController
{
public function conceptAction(Concept $concept, string $targetUrl): ResponseInterface
public function conceptAction(Concept $concept, string $targetUrl, ?Product $product = null): ResponseInterface
{
// A product can have its own text; title, image and button stay those of the concept
$popupText = (string)$product?->getPopupText();
if ('' === trim(strip_tags($popupText))) {
$popupText = $concept->getPopupText();
}
$this->view->assignMultiple([
'concept' => $concept,
'popupText' => $popupText,
'targetUrl' => $targetUrl,
]);
return $this->htmlResponse();
@@ -2,33 +2,8 @@
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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\ProductRepository;
use EP\EpProducts\Service\FilterService;
use EP\EpProducts\Service\SearchResultService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -36,36 +11,10 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxWatchlistController extends ActionController
{
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var SearchResultService
*/
protected $searchResultService;
/**
* @var FilterService
*/
protected $filterService;
/**
* @param ProductRepository $productRepository
* @param SearchResultService $searchResultService
* @param FilterService $filterService
*/
public function __construct
(
ProductRepository $productRepository,
SearchResultService $searchResultService,
FilterService $filterService
)
{
$this->productRepository = $productRepository;
$this->searchResultService = $searchResultService;
$this->filterService = $filterService;
public function __construct(
private readonly ProductRepository $productRepository,
private readonly SearchResultService $searchResultService,
) {
}
public function initializeAction(): void
@@ -77,8 +26,6 @@ class AjaxWatchlistController extends ActionController
}
/**
* @param array $watchlistUids
* @return string
* @throws \Doctrine\DBAL\DBALException
*/
public function listAction(array $watchlistUids = []): ResponseInterface
@@ -2,30 +2,6 @@
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;
@@ -35,24 +11,10 @@ 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;
public function __construct(
private readonly CityRepository $cityRepository,
private readonly ProductDataService $productDataService,
) {
}
/**
@@ -60,7 +22,7 @@ class CityController extends ActionController
*/
public function detailAction(?City $city = null): ResponseInterface
{
if (null === $city) {
if (!$city instanceof City) {
$cityUid = ($this->settings['cityUid'] ?? '');
$city = $this->cityRepository->findByIdentifier($cityUid);
}
@@ -2,30 +2,6 @@
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;
@@ -37,31 +13,11 @@ 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;
public function __construct(
private readonly ConceptRepository $conceptRepository,
private readonly ProductDataService $productDataService,
private readonly PageRepository $pageRepository,
) {
}
/**
@@ -69,7 +25,7 @@ class ConceptController extends ActionController
*/
public function detailAction(?Concept $concept = null): ResponseInterface
{
if ($concept === null) {
if (!$concept instanceof Concept) {
$conceptUid = ($this->settings['conceptUid'] ?? '');
$concept = $this->conceptRepository->findByIdentifier($conceptUid);
}
@@ -2,30 +2,6 @@
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;
@@ -36,24 +12,10 @@ 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;
public function __construct(
private readonly CountryRepository $countryRepository,
private readonly ProductDataService $productDataService,
) {
}
/**
@@ -61,7 +23,7 @@ class CountryController extends ActionController
*/
public function detailAction(?Country $country = null): ResponseInterface
{
if ($country === null) {
if (!$country instanceof Country) {
$countryUid = ($this->settings['countryUid'] ?? '');
$country = $this->countryRepository->findByIdentifier($countryUid);
}
@@ -2,30 +2,6 @@
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;
@@ -33,17 +9,8 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class FaqController extends ActionController
{
/**
* @var FaqRepository
*/
protected $faqRepository;
/**
* @param FaqRepository $faqRepository
*/
public function __construct(FaqRepository $faqRepository)
public function __construct(private readonly FaqRepository $faqRepository)
{
$this->faqRepository = $faqRepository;
}
public function detailAction(): ResponseInterface
@@ -2,30 +2,6 @@
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;
@@ -39,44 +15,12 @@ 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;
public function __construct(
private readonly HotelDataService $hotelDataService,
private readonly HotelRepository $hotelRepository,
private readonly ProductRepository $productRepository,
private readonly ProductDataService $productDataService,
) {
}
/**
@@ -84,7 +28,7 @@ class HotelController extends ActionController
*/
public function detailAction(?Hotel $hotel = null): ResponseInterface
{
if ($hotel !== null) {
if ($hotel instanceof Hotel) {
$hotelUid = $hotel->getUid();
} else {
$hotelUid = ($this->settings['hotelUid'] ?? '');
@@ -0,0 +1,47 @@
<?php
namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Service\IcalService;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class IcalController extends ActionController
{
public function __construct(
private readonly IcalService $icalService,
) {
}
/**
* The feed is sent as a response of its own, past the page cache: a cached page would lose the
* Content-Disposition header, and availability must not be older than the MyE&P snapshot
*/
public function contingentsAction(Hotel $hotel): ResponseInterface
{
$calendar = $this->icalService->createContingentsFeed($hotel);
// An empty calendar would show the portals every day as free; an error keeps their last import
if (null === $calendar) {
throw new PropagateResponseException(
$this->responseFactory->createResponse(503)
->withHeader('Content-Type', 'text/plain; charset=utf-8')
->withHeader('Cache-Control', 'private, no-store')
->withHeader('Retry-After', '900')
->withBody($this->streamFactory->createStream('Availability temporarily unavailable')),
1761730800
);
}
throw new PropagateResponseException(
$this->responseFactory->createResponse()
->withHeader('Content-Type', 'text/calendar;charset=UTF-8')
->withHeader('Content-Disposition', sprintf('attachment; filename=%s.ical', $hotel->getPathSegment()))
->withHeader('Cache-Control', 'private, no-store')
->withBody($this->streamFactory->createStream($calendar)),
1761730801
);
}
}
@@ -2,30 +2,6 @@
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;
@@ -38,27 +14,10 @@ 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;
private readonly HotelRepository $hotelRepository,
private readonly ProductRepository $productRepository,
) {
}
public function generalAction(): ResponseInterface
@@ -113,7 +72,7 @@ class MapController extends ActionController
* @param int $pageUid
* @return string
*/
protected function getUriForPageUid($pageUid)
private function getUriForPageUid($pageUid)
{
/** @var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
@@ -2,31 +2,6 @@
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 EP\EpProducts\Domain\Model\Dto\NewsletterSubscriptionRequest;
use EP\EpProducts\MyEP\ApiClient;
use EP\EpProducts\MyEP\ApiException;
@@ -35,22 +10,18 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class NewsletterController extends ActionController
{
private ApiClient $apiClient;
public function __construct(ApiClient $apiClient)
public function __construct(private readonly ApiClient $apiClient)
{
$this->apiClient = $apiClient;
}
public function subscriptionFormAction(?NewsletterSubscriptionRequest $subscriptionRequest = null): ResponseInterface
{
if (null === $subscriptionRequest) {
$subscriptionRequest = new NewsletterSubscriptionRequest();
}
public function subscriptionFormAction(
?NewsletterSubscriptionRequest $subscriptionRequest = null,
): ResponseInterface {
$subscriptionRequest ??= new NewsletterSubscriptionRequest();
try {
$lists = $this->apiClient->getNewsletterLists();
} catch (ApiException $e) {
} catch (ApiException) {
$lists = [];
}
@@ -2,30 +2,6 @@
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;
@@ -2,30 +2,6 @@
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 TYPO3\CMS\Extbase\Http\ForwardResponse;
use Psr\Http\Message\ResponseInterface;
use EP\EpProducts\Domain\Model\Hotel;
@@ -44,74 +20,16 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ProductController extends ActionController
{
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var HotelRepository
*/
protected $hotelRepository;
/**
* @var FilterService
*/
protected $filterService;
/**
* @var ProductDataService
*/
protected $productDataService;
/**
* @var DateService
*/
protected $dateService;
/**
* @var HotelDataService
*/
protected $hotelDataService;
/**
* @var InfoboxRepository
*/
private $infoboxRepository;
/**
* @var PickupOverviewService
*/
private $pickupOverviewService;
/**
* @param ProductRepository $productRepository
* @param ProductDataService $productDataService
* @param HotelRepository $hotelRepository
* @param HotelDataService $hotelDataService
* @param DateService $dateService
* @param FilterService $filterService
* @param InfoboxRepository $infoboxRepository
* @param PickupOverviewService $pickupOverviewService
*/
public function __construct(
ProductRepository $productRepository,
ProductDataService $productDataService,
HotelRepository $hotelRepository,
HotelDataService $hotelDataService,
DateService $dateService,
FilterService $filterService,
InfoboxRepository $infoboxRepository,
PickupOverviewService $pickupOverviewService
private readonly ProductRepository $productRepository,
private readonly ProductDataService $productDataService,
private readonly HotelRepository $hotelRepository,
private readonly HotelDataService $hotelDataService,
private readonly DateService $dateService,
private readonly FilterService $filterService,
private readonly InfoboxRepository $infoboxRepository,
private readonly PickupOverviewService $pickupOverviewService,
) {
$this->productRepository = $productRepository;
$this->productDataService = $productDataService;
$this->hotelRepository = $hotelRepository;
$this->hotelDataService = $hotelDataService;
$this->dateService = $dateService;
$this->filterService = $filterService;
$this->infoboxRepository = $infoboxRepository;
$this->pickupOverviewService = $pickupOverviewService;
}
/**
@@ -129,7 +47,7 @@ class ProductController extends ActionController
$dateTo = null
): ResponseInterface {
// Get product to display from flexform settings if not provided via url
if ($product === null) {
if (!$product instanceof Product) {
$productUid = ($this->settings['productUid'] ?? null);
$product = $productUid ? $this->productRepository->findByIdentifier($productUid) : null;
}
@@ -155,7 +73,7 @@ class ProductController extends ActionController
$arguments = [
'product' => $product,
];
return (new ForwardResponse('hotelList'))->withArguments($arguments);
return new ForwardResponse('hotelList')->withArguments($arguments);
}
// Use first hotel which should be the only one at this point
if ($product->getHotels()->count() > 0) {
@@ -173,10 +91,6 @@ class ProductController extends ActionController
$isProductWithGroupInquiryForm = ($concept !== null) && \in_array($concept->getUid(), $groupInquiryFormConceptUids);
$nonBookableInfo = $product->getNonBookableInfo();
// Assemble backlink
$backlinkUrl = $this->request->getQueryParams()['ref'] ?? null;
$backLinkLabel = strpos((string)$backlinkUrl, 'suche') !== -1 ? 'zurück zum Suchergebnis' : 'zurück';
$this->view->assignMultiple([
'hotel' => $hotel,
'product' => $product,
@@ -188,8 +102,6 @@ class ProductController extends ActionController
'currentPageUid' => $this->request->getAttribute('frontend.page.information')->getId(),
'dateFrom' => $dateFrom,
'dateTo' => $dateTo,
'referringUrl' => $backlinkUrl,
'backlinkLabel' => $backLinkLabel,
'nonBookableInfo' => $nonBookableInfo,
'pickupsOverview' => $this->pickupOverviewService->getOverview($product, $hotel),
]);
@@ -213,9 +125,6 @@ class ProductController extends ActionController
return $this->htmlResponse();
}
/**
* @param Product $product
*/
public function hotelListAction(Product $product): ResponseInterface
{
$hotels = $this->hotelDataService->getList($product);
@@ -2,30 +2,6 @@
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;
@@ -36,24 +12,10 @@ 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;
public function __construct(
private readonly RegionRepository $regionRepository,
private readonly ProductDataService $productDataService,
) {
}
/**
@@ -61,14 +23,12 @@ class RegionController extends ActionController
*/
public function detailAction(?Region $region = null): ResponseInterface
{
if ($region === null) {
if (!$region instanceof Region) {
$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);
$additionalRegionUids = array_map(fn($uid): int => (int)$uid, $additionalRegionUids);
$teaserRegionUids = array_merge([$region->getUid()], $additionalRegionUids);
$filterSettings = [
'regionUids' => $teaserRegionUids,
@@ -2,30 +2,6 @@
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\Service\FilterService;
use EP\EpProducts\Service\FilterSettingsEncoder;
@@ -46,26 +22,11 @@ class SearchController extends ActionController
*/
public const SEARCHBAR_PARAMETERS = ['dateFrom', 'dateTo', 'pax', 'destinationType', 'destinationUid', 'priceRange'];
/**
* @var FilterService
*/
protected $filterService;
/**
* @var SearchResultService
*/
private $searchResultService;
/**
* @param FilterService $filterService
*/
public function __construct(FilterService $filterService, SearchResultService $searchResultService)
public function __construct(private FilterService $filterService, private SearchResultService $searchResultService)
{
$this->filterService = $filterService;
$this->searchResultService = $searchResultService;
}
public function initializeSearchresultAction()
public function initializeSearchresultAction(): void
{
// Process encoded filtersettings from static search result.
// Explicit filter settings win: they carry the visitor's current selection,
@@ -81,7 +42,7 @@ class SearchController extends ActionController
// The fields of the search bar, which sends them here by GET
$searchbarParameters = array_filter(
array_intersect_key($this->request->getQueryParams(), array_flip(self::SEARCHBAR_PARAMETERS)),
'is_string'
is_string(...)
);
// Anything but a real Y-m-d date is dropped: the URL is easy to edit, and
// DateUtility::formatDateRange() throws on what DateTime cannot read
@@ -100,9 +61,6 @@ class SearchController extends ActionController
$this->processFilterSettingsArgument($forcedConceptUids);
}
/**
* @param array $filterSettings
*/
public function searchresultAction(array $filterSettings): ResponseInterface
{
$dateSelected = false;
@@ -2,31 +2,6 @@
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 EP\EpProducts\Service\FilterService;
use EP\EpProducts\Service\SearchResultService;
use Psr\Http\Message\ResponseInterface;
@@ -2,30 +2,6 @@
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;
@@ -34,17 +10,8 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class TeamController extends ActionController
{
/**
* @var TeammemberRepository
*/
protected $teammemberRepository;
/**
* @param TeammemberRepository $teammemberRepository
*/
public function __construct(TeammemberRepository $teammemberRepository)
public function __construct(private readonly TeammemberRepository $teammemberRepository)
{
$this->teammemberRepository = $teammemberRepository;
}
public function listAction(): ResponseInterface
@@ -66,7 +33,7 @@ class TeamController extends ActionController
*/
public function showAction(?Teammember $member = null): ResponseInterface
{
if ($member === null) {
if (!$member instanceof Teammember) {
return $this->redirect('list', null, null, null, null, 0, 302);
}
@@ -77,8 +44,6 @@ class TeamController extends ActionController
}
/**
* @param Teammember $member
*
* @return string
*/
public function coalesceHeader(Teammember $member)
@@ -14,22 +14,12 @@ use TYPO3\CMS\Frontend\Controller\ErrorController;
class TravelinfoController extends ActionController
{
private const SELECTION_ID_BOARD = 71;
private const int SELECTION_ID_BOARD = 71;
/**
* @var ApiClient
*/
private $apiClient;
/**
* @var TravelinfoRepository
*/
private $travelinfoRepository;
public function __construct(ApiClient $apiClient, TravelinfoRepository $travelinfoRepository)
{
$this->apiClient = $apiClient;
$this->travelinfoRepository = $travelinfoRepository;
public function __construct(
private readonly ApiClient $apiClient,
private readonly TravelinfoRepository $travelinfoRepository,
) {
}
public function travelCodeAction(string $travelCode): ResponseInterface
@@ -55,7 +45,7 @@ class TravelinfoController extends ActionController
public function travelInfoAction(?Travelinfo $travelInfo = null): ResponseInterface
{
// the page itself (/reiseinformationen/) without a travel info or code
if (null === $travelInfo) {
if (!$travelInfo instanceof Travelinfo) {
$this->notFound();
}
@@ -74,26 +64,24 @@ class TravelinfoController extends ActionController
throw new ImmediateResponseException($response, 1731071856);
}
private function handleRequest(string $travelCode, Travelinfo $travelInfo)
private function handleRequest(string $travelCode, Travelinfo $travelInfo): void
{
// fetch travel data via API call
try {
$travelData = $this->apiClient->getTravel($travelCode);
} catch (ApiException $e) {
} catch (ApiException) {
$travelData = [];
}
try {
$pickupsPlanning = $this->apiClient->getPickupsPlanning($travelCode);
} catch (ApiException $e) {
} catch (ApiException) {
$pickupsPlanning = [];
}
// extract courses from additional services
$courses = array_filter($travelData['additionalServices'] ?? [], function ($service) {
return 'KUR' === $service['subType'];
});
$courses = array_filter($travelData['additionalServices'] ?? [], fn($service): bool => 'KUR' === $service['subType']);
// extract board services from included services
$includedServicesBoard = $travelData['selectionGroups'][self::SELECTION_ID_BOARD] ?? [];
@@ -101,9 +89,7 @@ class TravelinfoController extends ActionController
// extract bus departure time if applicable
$departureDayTime = null;
$service = array_filter($travelData['transportationServices'] ?? [], function ($service) {
return 'BUS' === $service['subType'] && 'RUECK' === $service['direction'];
});
$service = array_filter($travelData['transportationServices'] ?? [], fn($service): bool => 'BUS' === $service['subType'] && 'RUECK' === $service['direction']);
if ($serviceDetail = reset($service)) {
$departureDayTime = $serviceDetail['dayTime'];
}
@@ -2,30 +2,6 @@
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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;
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Dashboard;
use EP\EpProducts\MyEP\ApiClient;
use EP\EpProducts\Service\ApiImportSource;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\View\BackendViewFactory;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Dashboard\Widgets\RequestAwareWidgetInterface;
use TYPO3\CMS\Dashboard\Widgets\WidgetConfigurationInterface;
use TYPO3\CMS\Dashboard\Widgets\WidgetInterface;
/**
* When the imports last ran and what data they brought, replacing the TYPO3 10 system information entry
* "Letzer Produktdaten-Import"
*/
final class ImportStatusWidget implements WidgetInterface, RequestAwareWidgetInterface
{
/**
* Command => label; the scheduler tasks are found by the command they run, not by uid
*/
private const array COMMANDS = [
'ep:import-dates' => 'Reisedaten (MyE&P)',
'ep:import-snowreport' => 'Schneehöhen',
'ep:groups-swiss' => 'groups.swiss',
];
/**
* A task whose next run is this far in the past means scheduler:run is not called any more
*/
private const int OVERDUE_SECONDS = 3600;
private ServerRequestInterface $request;
public function __construct(
private readonly WidgetConfigurationInterface $configuration,
private readonly BackendViewFactory $backendViewFactory,
private readonly ConnectionPool $connectionPool,
private readonly ApiImportSource $importSource,
private readonly ApiClient $apiClient,
private readonly array $options = [],
) {
}
public function setRequest(ServerRequestInterface $request): void
{
$this->request = $request;
}
public function renderWidgetContent(): string
{
$tasks = $this->findTasks();
$rows = [];
foreach (self::COMMANDS as $command => $label) {
$rows[$command] = ['label' => $label, 'dataAt' => null, 'task' => $tasks[$command] ?? null];
}
$rows['ep:import-dates']['dataAt'] = $this->importSource->getImportedTimestamp()?->getTimestamp();
$rows['ep:import-snowreport']['dataAt'] = $this->findLastSnowreportUpdate();
$myEpLastUpdate = null;
$myEpUnreachable = false;
try {
$myEpLastUpdate = $this->apiClient->getLastUpdateAt()?->getTimestamp();
} catch (\Throwable) {
$myEpUnreachable = true;
}
$view = $this->backendViewFactory->create($this->request, ['typo3/cms-dashboard', 'ep/ep-products']);
$view->assignMultiple([
'rows' => $rows,
'myEpLastUpdate' => $myEpLastUpdate,
'myEpUnreachable' => $myEpUnreachable,
'importPending' => null !== $myEpLastUpdate && $myEpLastUpdate > (int)$rows['ep:import-dates']['dataAt'],
'configuration' => $this->configuration,
]);
return $view->render('Widget/ImportStatusWidget');
}
public function getOptions(): array
{
return $this->options;
}
/**
* @return array<string, array{uid: int, disabled: bool, lastRun: int, nextRun: int, overdue: bool, failure: ?string}>
*/
private function findTasks(): array
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_scheduler_task');
$result = $queryBuilder
->select('uid', 'disable', 'lastexecution_time', 'lastexecution_failure', 'nextexecution', 'serialized_task_object')
->from('tx_scheduler_task')
->where($queryBuilder->expr()->eq('deleted', 0))
->orderBy('uid')
->executeQuery();
$tasks = [];
while ($row = $result->fetchAssociative()) {
foreach (array_keys(self::COMMANDS) as $command) {
// The command identifier appears in the serialized ExecuteSchedulableCommandTask exactly as
// serialize() writes the string; matching it avoids unserializing task objects
if (!isset($tasks[$command]) && str_contains((string)$row['serialized_task_object'], serialize($command))) {
$tasks[$command] = [
'uid' => (int)$row['uid'],
'disabled' => (bool)$row['disable'],
'lastRun' => (int)$row['lastexecution_time'],
'nextRun' => (int)$row['nextexecution'],
'overdue' => !$row['disable'] && 0 < (int)$row['nextexecution']
&& (int)$row['nextexecution'] < time() - self::OVERDUE_SECONDS,
'failure' => $this->failureMessage((string)$row['lastexecution_failure']),
];
}
}
}
return $tasks;
}
private function failureMessage(string $failure): ?string
{
if ('' === $failure) {
return null;
}
// Written by the scheduler as serialize(['code' => …, 'message' => …, …])
$data = @unserialize($failure, ['allowed_classes' => false]);
return is_array($data) && '' !== (string)($data['message'] ?? '') ? (string)$data['message'] : 'Unbekannter Fehler';
}
private function findLastSnowreportUpdate(): ?int
{
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_epproducts_domain_model_snowreport');
$queryBuilder->getRestrictions()->removeAll();
$tstamp = $queryBuilder
->selectLiteral('MAX(tstamp)')
->from('tx_epproducts_domain_model_snowreport')
->executeQuery()
->fetchOne();
return $tstamp ? (int)$tstamp : null;
}
}
@@ -6,30 +6,6 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/***************************************************************
*
* 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 AbstractDestination extends AbstractEntity
{
@@ -139,7 +115,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $detailPage
*/
public function setDetailPage($detailPage)
public function setDetailPage($detailPage): void
{
$this->detailPage = $detailPage;
}
@@ -155,7 +131,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $season
*/
public function setSeason($season)
public function setSeason($season): void
{
$this->season = $season;
}
@@ -181,7 +157,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param float $latitude
*/
public function setLatitude($latitude)
public function setLatitude($latitude): void
{
$this->latitude = $latitude;
}
@@ -197,7 +173,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param float $longitude
*/
public function setLongitude($longitude)
public function setLongitude($longitude): void
{
$this->longitude = $longitude;
}
@@ -213,7 +189,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -229,7 +205,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $nameInternal
*/
public function setNameInternal($nameInternal)
public function setNameInternal($nameInternal): void
{
$this->nameInternal = $nameInternal;
}
@@ -245,7 +221,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $keywords
*/
public function setKeywords($keywords)
public function setKeywords($keywords): void
{
$this->keywords = $keywords;
}
@@ -261,7 +237,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $headline
*/
public function setHeadline($headline)
public function setHeadline($headline): void
{
$this->headline = $headline;
}
@@ -277,7 +253,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $teaser
*/
public function setTeaser($teaser)
public function setTeaser($teaser): void
{
$this->teaser = $teaser;
}
@@ -293,7 +269,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $headerTitle
*/
public function setHeaderTitle($headerTitle)
public function setHeaderTitle($headerTitle): void
{
$this->headerTitle = $headerTitle;
}
@@ -309,7 +285,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $headerSubtitle
*/
public function setHeaderSubtitle($headerSubtitle)
public function setHeaderSubtitle($headerSubtitle): void
{
$this->headerSubtitle = $headerSubtitle;
}
@@ -325,7 +301,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $description
*/
public function setDescription($description)
public function setDescription($description): void
{
$this->description = $description;
}
@@ -341,7 +317,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param string $descriptionExtended
*/
public function setDescriptionExtended($descriptionExtended)
public function setDescriptionExtended($descriptionExtended): void
{
$this->descriptionExtended = $descriptionExtended;
}
@@ -357,7 +333,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param ObjectStorage<FileReference> $images
*/
public function setImages(ObjectStorage $images)
public function setImages(ObjectStorage $images): void
{
$this->images = $images;
}
@@ -385,7 +361,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param ObjectStorage $teaserImages
*/
public function setTeaserImages($teaserImages)
public function setTeaserImages($teaserImages): void
{
$this->teaserImages = $teaserImages;
}
@@ -415,7 +391,7 @@ class AbstractDestination extends AbstractEntity
/**
* @param ObjectStorage $headerImages
*/
public function setHeaderImages($headerImages)
public function setHeaderImages($headerImages): void
{
$this->headerImages = $headerImages;
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Badge extends AbstractEntity
{
@@ -57,7 +35,7 @@ class Badge extends AbstractEntity
/**
* @param string $title
*/
public function setTitle($title)
public function setTitle($title): void
{
$this->title = $title;
}
@@ -73,7 +51,7 @@ class Badge extends AbstractEntity
/**
* @param string $subtitle
*/
public function setSubtitle($subtitle)
public function setSubtitle($subtitle): void
{
$this->subtitle = $subtitle;
}
@@ -89,10 +67,9 @@ class Badge extends AbstractEntity
/**
* @param string $link
*/
public function setLink($link)
public function setLink($link): void
{
$this->link = $link;
}
}
@@ -4,30 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* 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 City extends AbstractDestination implements TeaserInterface
{
@@ -83,7 +59,7 @@ class City extends AbstractDestination implements TeaserInterface
/**
* @param bool $excludeFromTeasergroups
*/
public function setExcludeFromTeasergroups($excludeFromTeasergroups)
public function setExcludeFromTeasergroups($excludeFromTeasergroups): void
{
$this->excludeFromTeasergroups = (bool) $excludeFromTeasergroups;
}
@@ -98,7 +74,7 @@ class City extends AbstractDestination implements TeaserInterface
/**
* @param string $party
*/
public function setParty($party) {
public function setParty($party): void {
$this->party = $party;
}
@@ -112,7 +88,7 @@ class City extends AbstractDestination implements TeaserInterface
/**
* @param string $leisure
*/
public function setLeisure($leisure) {
public function setLeisure($leisure): void {
$this->leisure = $leisure;
}
@@ -127,7 +103,7 @@ class City extends AbstractDestination implements TeaserInterface
$headerImages = $this->headerImages;
if ($headerImages->count() === 0 && null !== $this->getRegion()) {
$headerImages = $this->getRegion()->getHeaderImages();
return $this->getRegion()->getHeaderImages();
}
return $headerImages;
@@ -141,10 +117,7 @@ class City extends AbstractDestination implements TeaserInterface
return $this->country;
}
/**
* @param Country $country
*/
public function setCountry(Country $country)
public function setCountry(Country $country): void
{
$this->country = $country;
}
@@ -157,10 +130,7 @@ class City extends AbstractDestination implements TeaserInterface
return $this->region;
}
/**
* @param Region $region
*/
public function setRegion(Region $region)
public function setRegion(Region $region): void
{
$this->region = $region;
}
@@ -176,7 +146,7 @@ class City extends AbstractDestination implements TeaserInterface
/**
* @param ObjectStorage<Fact> $facts
*/
public function setFacts(ObjectStorage $facts)
public function setFacts(ObjectStorage $facts): void
{
$this->facts = $facts;
}
@@ -192,7 +162,7 @@ class City extends AbstractDestination implements TeaserInterface
/**
* @param Officetip $officetip
*/
public function setOfficetip($officetip)
public function setOfficetip($officetip): void
{
$this->officetip = $officetip;
}
@@ -6,30 +6,6 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/***************************************************************
*
* 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 Concept extends AbstractEntity implements TeaserInterface
{
@@ -146,7 +122,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param int $busProId
*/
public function setBusProId($busProId) {
public function setBusProId($busProId): void {
$this->busProId = $busProId;
}
@@ -161,7 +137,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -177,7 +153,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
@@ -193,7 +169,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $teaser
*/
public function setTeaser($teaser)
public function setTeaser($teaser): void
{
$this->teaser = $teaser;
}
@@ -203,7 +179,7 @@ class Concept extends AbstractEntity implements TeaserInterface
return $this->popupText;
}
public function setPopupText($popupText)
public function setPopupText($popupText): static
{
$this->popupText = $popupText;
@@ -215,7 +191,7 @@ class Concept extends AbstractEntity implements TeaserInterface
return $this->popupTitle;
}
public function setPopupTitle($popupTitle)
public function setPopupTitle($popupTitle): static
{
$this->popupTitle = $popupTitle;
@@ -227,7 +203,7 @@ class Concept extends AbstractEntity implements TeaserInterface
return $this->popupButtonLabel;
}
public function setPopupButtonLabel($popupButtonLabel)
public function setPopupButtonLabel($popupButtonLabel): static
{
$this->popupButtonLabel = $popupButtonLabel;
@@ -245,7 +221,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $headline
*/
public function setHeadline($headline)
public function setHeadline($headline): void
{
$this->headline = $headline;
}
@@ -261,7 +237,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $headerTitle
*/
public function setHeaderTitle($headerTitle)
public function setHeaderTitle($headerTitle): void
{
$this->headerTitle = $headerTitle;
}
@@ -277,7 +253,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $headerSubtitle
*/
public function setHeaderSubtitle($headerSubtitle)
public function setHeaderSubtitle($headerSubtitle): void
{
$this->headerSubtitle = $headerSubtitle;
}
@@ -293,7 +269,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $description
*/
public function setDescription($description)
public function setDescription($description): void
{
$this->description = $description;
}
@@ -309,7 +285,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $descriptionExtended
*/
public function setDescriptionExtended($descriptionExtended)
public function setDescriptionExtended($descriptionExtended): void
{
$this->descriptionExtended = $descriptionExtended;
}
@@ -325,7 +301,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param string $detailPage
*/
public function setDetailPage($detailPage)
public function setDetailPage($detailPage): void
{
$this->detailPage = $detailPage;
}
@@ -353,7 +329,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $teaserImages
*/
public function setTeaserImages($teaserImages)
public function setTeaserImages($teaserImages): void
{
$this->teaserImages = $teaserImages;
}
@@ -381,7 +357,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $headerImages
*/
public function setHeaderImages($headerImages)
public function setHeaderImages($headerImages): void
{
$this->headerImages = $headerImages;
}
@@ -397,23 +373,17 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $images
*/
public function setImages($images)
public function setImages($images): void
{
$this->images = $images;
}
/**
* @param Product $product
*/
public function addProduct(Product $product)
public function addProduct(Product $product): void
{
$this->products->attach($product);
}
/**
* @param Product $product
*/
public function removeProduct(Product $product)
public function removeProduct(Product $product): void
{
$this->products->detach($product);
}
@@ -429,7 +399,7 @@ class Concept extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<Product> $products
*/
public function setProducts(ObjectStorage $products)
public function setProducts(ObjectStorage $products): void
{
$this->products = $products;
}
@@ -4,30 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* 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 Country extends AbstractDestination implements TeaserInterface
{
@@ -39,6 +15,7 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @var string
*/
#[\Override]
protected $descriptionExtended = '';
/**
@@ -72,7 +49,7 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @param string $code
*/
public function setCode($code) {
public function setCode($code): void {
$this->code = $code;
}
@@ -87,23 +64,17 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @param string $descriptionExtended
*/
public function setDescriptionExtended($descriptionExtended)
public function setDescriptionExtended($descriptionExtended): void
{
$this->descriptionExtended = $descriptionExtended;
}
/**
* @param Fact $fact
*/
public function addFact(Fact $fact)
public function addFact(Fact $fact): void
{
$this->facts->attach($fact);
}
/**
* @param Fact $factToRemove
*/
public function removeFact(Fact $factToRemove)
public function removeFact(Fact $factToRemove): void
{
$this->facts->detach($factToRemove);
}
@@ -119,7 +90,7 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @param ObjectStorage<Fact> $facts
*/
public function setFacts(ObjectStorage $facts)
public function setFacts(ObjectStorage $facts): void
{
$this->facts = $facts;
}
@@ -135,7 +106,7 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @param Officetip $officetip
*/
public function setOfficetip($officetip)
public function setOfficetip($officetip): void
{
$this->officetip = $officetip;
}
@@ -151,7 +122,7 @@ class Country extends AbstractDestination implements TeaserInterface
/**
* @param Snowreport $snowreport
*/
public function setSnowreport($snowreport)
public function setSnowreport($snowreport): void
{
$this->snowreport = $snowreport;
}
@@ -4,30 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Date extends AbstractEntity
{
/**
@@ -190,7 +166,7 @@ class Date extends AbstractEntity
return $this->product;
}
public function setProduct($product)
public function setProduct($product): void
{
$this->product = $product;
}
@@ -200,7 +176,7 @@ class Date extends AbstractEntity
return $this->hotel;
}
public function setHotel($hotel)
public function setHotel($hotel): void
{
$this->hotel = $hotel;
}
@@ -210,7 +186,7 @@ class Date extends AbstractEntity
return $this->country;
}
public function setCountry($country)
public function setCountry($country): void
{
$this->country = $country;
}
@@ -220,7 +196,7 @@ class Date extends AbstractEntity
return $this->region;
}
public function setRegion($region)
public function setRegion($region): void
{
$this->region = $region;
}
@@ -230,7 +206,7 @@ class Date extends AbstractEntity
return $this->city;
}
public function setCity($city)
public function setCity($city): void
{
$this->city = $city;
}
@@ -246,7 +222,7 @@ class Date extends AbstractEntity
/**
* @param int $busProId
*/
public function setBusProId($busProId)
public function setBusProId($busProId): void
{
$this->busProId = $busProId;
}
@@ -262,7 +238,7 @@ class Date extends AbstractEntity
/**
* @param int $hotelBusProId
*/
public function setHotelBusProId($hotelBusProId)
public function setHotelBusProId($hotelBusProId): void
{
$this->hotelBusProId = $hotelBusProId;
}
@@ -278,7 +254,7 @@ class Date extends AbstractEntity
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
@@ -294,7 +270,7 @@ class Date extends AbstractEntity
/**
* @param bool $daytrip
*/
public function setDaytrip($daytrip)
public function setDaytrip($daytrip): void
{
$this->daytrip = $daytrip;
}
@@ -307,10 +283,7 @@ class Date extends AbstractEntity
return $this->dateStart;
}
/**
* @param \DateTime $dateStart
*/
public function setDateStart(\DateTime $dateStart)
public function setDateStart(\DateTime $dateStart): void
{
$this->dateStart = $dateStart;
}
@@ -323,10 +296,7 @@ class Date extends AbstractEntity
return $this->dateEnd;
}
/**
* @param \DateTime $dateEnd
*/
public function setDateEnd(\DateTime $dateEnd)
public function setDateEnd(\DateTime $dateEnd): void
{
$this->dateEnd = $dateEnd;
}
@@ -342,7 +312,7 @@ class Date extends AbstractEntity
/**
* @param int $nights
*/
public function setNights($nights)
public function setNights($nights): void
{
$this->nights = $nights;
}
@@ -358,7 +328,7 @@ class Date extends AbstractEntity
/**
* @param string $board
*/
public function setBoard($board)
public function setBoard($board): void
{
$this->board = $board;
}
@@ -371,10 +341,7 @@ class Date extends AbstractEntity
return $this->boardCode;
}
/**
* @param string $boardCode
*/
public function setBoardCode(string $boardCode)
public function setBoardCode(string $boardCode): void
{
$this->boardCode = $boardCode;
}
@@ -387,10 +354,7 @@ class Date extends AbstractEntity
return $this->boardBusProId;
}
/**
* @param int $boardBusProId
*/
public function setBoardBusProId(int $boardBusProId)
public function setBoardBusProId(int $boardBusProId): void
{
$this->boardBusProId = $boardBusProId;
}
@@ -406,7 +370,7 @@ class Date extends AbstractEntity
/**
* @param bool $busIncluded
*/
public function setBusIncluded($busIncluded)
public function setBusIncluded($busIncluded): void
{
$this->busIncluded = $busIncluded;
}
@@ -422,7 +386,7 @@ class Date extends AbstractEntity
/**
* @param bool $skipassIncluded
*/
public function setSkipassIncluded($skipassIncluded)
public function setSkipassIncluded($skipassIncluded): void
{
$this->skipassIncluded = $skipassIncluded;
}
@@ -438,7 +402,7 @@ class Date extends AbstractEntity
/**
* @param bool $groupTravel
*/
public function setGroupTravel($groupTravel)
public function setGroupTravel($groupTravel): void
{
$this->groupTravel = $groupTravel;
}
@@ -454,7 +418,7 @@ class Date extends AbstractEntity
/**
* @param bool $busAvailable
*/
public function setBusAvailable($busAvailable)
public function setBusAvailable($busAvailable): void
{
$this->busAvailable = $busAvailable;
}
@@ -470,7 +434,7 @@ class Date extends AbstractEntity
/**
* @param float $busPrice
*/
public function setBusPrice($busPrice)
public function setBusPrice($busPrice): void
{
$this->busPrice = $busPrice;
}
@@ -478,15 +442,12 @@ class Date extends AbstractEntity
/**
* @return array $servicesIncluded
*/
public function getServicesIncluded()
public function getServicesIncluded(): mixed
{
return unserialize($this->servicesIncluded);
}
/**
* @param array $servicesIncluded
*/
public function setServicesIncluded(array $servicesIncluded)
public function setServicesIncluded(array $servicesIncluded): void
{
$this->servicesIncluded = serialize($servicesIncluded);
}
@@ -505,20 +466,17 @@ class Date extends AbstractEntity
return $services;
}
/**
* @param array $servicesGeneral
*/
public function setServicesGeneral(array $servicesGeneral)
public function setServicesGeneral(array $servicesGeneral): void
{
$this->servicesGeneral = serialize($servicesGeneral);
}
public function getCoursesIncluded()
public function getCoursesIncluded(): mixed
{
return unserialize($this->coursesIncluded);
}
public function setCoursesIncluded($coursesIncluded)
public function setCoursesIncluded($coursesIncluded): void
{
$this->coursesIncluded = $coursesIncluded;
}
@@ -528,7 +486,7 @@ class Date extends AbstractEntity
return $this->getServicesGeneral('KUR');
}
public function getGuide()
public function getGuide(): mixed
{
return unserialize($this->guide);
}
@@ -541,15 +499,12 @@ class Date extends AbstractEntity
/**
* @return string
*/
public function getPickups()
public function getPickups(): mixed
{
return unserialize($this->pickups);
}
/**
* @param array $pickups
*/
public function setPickups(array $pickups)
public function setPickups(array $pickups): void
{
$this->pickups = serialize($pickups);
}
@@ -564,7 +519,7 @@ class Date extends AbstractEntity
/**
* @param float $discount
*/
public function setDiscount($discount) {
public function setDiscount($discount): void {
$this->discount = $discount;
}
@@ -579,7 +534,7 @@ class Date extends AbstractEntity
/**
* @param float $minPrice
*/
public function setMinPrice($minPrice)
public function setMinPrice($minPrice): void
{
$this->minPrice = $minPrice;
}
@@ -595,7 +550,7 @@ class Date extends AbstractEntity
/**
* @param float $pseudoPrice
*/
public function setPseudoPrice($pseudoPrice)
public function setPseudoPrice($pseudoPrice): void
{
$this->pseudoPrice = $pseudoPrice;
}
@@ -611,7 +566,7 @@ class Date extends AbstractEntity
/**
* @param int $available
*/
public function setAvailable($available)
public function setAvailable($available): void
{
$this->available = $available;
}
@@ -621,7 +576,7 @@ class Date extends AbstractEntity
return $this->minPax;
}
public function setMinPax($minPax)
public function setMinPax($minPax): static
{
$this->minPax = $minPax;
@@ -633,7 +588,7 @@ class Date extends AbstractEntity
return $this->new;
}
public function setNew(bool $new)
public function setNew(bool $new): void
{
$this->new = $new;
}
@@ -2,31 +2,6 @@
namespace EP\EpProducts\Domain\Model\Dto;
/***************************************************************
*
* 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 FilterOptions implements \JsonSerializable
{
/**
@@ -44,67 +19,55 @@ class FilterOptions implements \JsonSerializable
/**
* @var array
*/
protected $priceRanges = [];
private $priceRanges = [];
private array $destinations = [];
/**
* @var array
*/
protected $destinations = [];
private $concepts = [];
private array $pax;
/**
* @var array
*/
protected $concepts = [];
private $nights = [];
/**
* @var array
*/
protected $pax = [];
private $hotelTypes = [];
/**
* @var array
*/
protected $nights = [];
private $boardTypes = [];
/**
* @var array
*/
protected $hotelTypes = [];
/**
* @var array
*/
protected $boardTypes = [];
/**
* @var array
*/
protected $roomTypes = [];
private $roomTypes = [];
/**
* @var \DateTime
*/
protected $dateFrom;
private $dateFrom;
/**
* @var \DateTime
*/
protected $dateTo;
private $dateTo;
/**
* @var bool
*/
protected $busAvailable;
private $busAvailable;
/**
* @var int
*/
protected $resultCount;
private ?int $resultCount = null;
/**
* @var string
*/
protected $resultsUri;
private ?string $resultsUri = null;
public function __construct()
{
@@ -125,7 +88,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $priceRanges
*/
public function setPriceRanges($priceRanges)
public function setPriceRanges($priceRanges): void
{
$this->priceRanges = $priceRanges;
}
@@ -138,10 +101,7 @@ class FilterOptions implements \JsonSerializable
return $this->destinations;
}
/**
* @param array $destinations
*/
public function setDestinations(array $destinations)
public function setDestinations(array $destinations): void
{
$this->destinations = $destinations;
}
@@ -157,7 +117,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $concepts
*/
public function setConcepts($concepts)
public function setConcepts($concepts): void
{
$this->concepts = $concepts;
}
@@ -173,7 +133,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $pax
*/
public function setPax($pax)
public function setPax($pax): void
{
$this->pax = $pax;
}
@@ -189,7 +149,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $nights
*/
public function setNights($nights)
public function setNights($nights): void
{
$this->nights = $nights;
}
@@ -205,7 +165,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $hotelTypes
*/
public function setHotelTypes($hotelTypes)
public function setHotelTypes($hotelTypes): void
{
$this->hotelTypes = $hotelTypes;
}
@@ -221,7 +181,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $boardTypes
*/
public function setBoardTypes($boardTypes)
public function setBoardTypes($boardTypes): void
{
$this->boardTypes = $boardTypes;
}
@@ -237,7 +197,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param array $roomTypes
*/
public function setRoomTypes($roomTypes)
public function setRoomTypes($roomTypes): void
{
$this->roomTypes = $roomTypes;
}
@@ -253,7 +213,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param \DateTime $dateFrom
*/
public function setDateFrom($dateFrom)
public function setDateFrom($dateFrom): void
{
$this->dateFrom = $dateFrom;
}
@@ -269,7 +229,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param \DateTime $dateTo
*/
public function setDateTo($dateTo)
public function setDateTo($dateTo): void
{
$this->dateTo = $dateTo;
}
@@ -285,7 +245,7 @@ class FilterOptions implements \JsonSerializable
/**
* @param bool $busAvailable
*/
public function setBusAvailable($busAvailable)
public function setBusAvailable($busAvailable): void
{
$this->busAvailable = $busAvailable;
}
@@ -298,10 +258,7 @@ class FilterOptions implements \JsonSerializable
return $this->resultCount;
}
/**
* @param int $resultCount
*/
public function setResultCount(int $resultCount)
public function setResultCount(int $resultCount): void
{
$this->resultCount = $resultCount;
}
@@ -314,10 +271,7 @@ class FilterOptions implements \JsonSerializable
return $this->resultsUri;
}
/**
* @param string $resultsUri
*/
public function setResultsUri(string $resultsUri)
public function setResultsUri(string $resultsUri): void
{
$this->resultsUri = $resultsUri;
}
@@ -98,9 +98,7 @@ class NewsletterSubscriptionRequest implements JsonSerializable
'firstName' => $this->firstName,
'lastName' => $this->lastName,
'email' => $this->email,
'listIds' => array_map(function($value) {
return (int) $value;
}, $this->listIds),
'listIds' => array_map(fn($value): int => (int) $value, $this->listIds),
];
}
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Fact extends AbstractEntity
{
@@ -57,7 +35,7 @@ class Fact extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -73,7 +51,7 @@ class Fact extends AbstractEntity
/**
* @param string $keywords
*/
public function setKeywords($keywords)
public function setKeywords($keywords): void
{
$this->keywords = $keywords;
}
@@ -89,7 +67,7 @@ class Fact extends AbstractEntity
/**
* @param string $label
*/
public function setLabel($label)
public function setLabel($label): void
{
$this->label = $label;
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Faq extends AbstractEntity
{
@@ -52,7 +30,7 @@ class Faq extends AbstractEntity
/**
* @param string $question
*/
public function setQuestion($question)
public function setQuestion($question): void
{
$this->question = $question;
}
@@ -68,9 +46,9 @@ class Faq extends AbstractEntity
/**
* @param string $answer
*/
public function setAnswer($answer)
public function setAnswer($answer): void
{
$this->answer = $answer;
}
}
}
@@ -4,31 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* Copyright notice
*
* (c) 2020 Björn Fromme <[email protected]>, dreipunktnull
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
class GroupsPriceBoard extends AbstractEntity implements \JsonSerializable
{
/**
@@ -46,33 +21,21 @@ class GroupsPriceBoard extends AbstractEntity implements \JsonSerializable
*/
protected $priceChf;
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* @return float
*/
public function getPrice(): float
{
return $this->price;
}
/**
* @param float $price
*/
public function setPrice(float $price): void
{
$this->price = $price;
@@ -86,17 +49,11 @@ class GroupsPriceBoard extends AbstractEntity implements \JsonSerializable
return $this->priceChf;
}
/**
* @param float $price
*/
public function setPriceChf(float $price): void
{
$this->priceChf = $price;
}
/**
* @return array
*/
public function jsonSerialize(): array
{
return [
@@ -4,31 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* Copyright notice
*
* (c) 2020 Björn Fromme <[email protected]>, dreipunktnull
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
{
/**
@@ -66,65 +41,41 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
*/
protected $priceAdditionalPersonChf;
/**
* @return \DateTime
*/
public function getDateFrom(): \DateTime
{
return $this->dateFrom;
}
/**
* @param \DateTime $dateFrom
*/
public function setDateFrom(\DateTime $dateFrom): void
{
$this->dateFrom = $dateFrom;
}
/**
* @return \DateTime
*/
public function getDateTo(): \DateTime
{
return $this->dateTo;
}
/**
* @param \DateTime $dateTo
*/
public function setDateTo(\DateTime $dateTo): void
{
$this->dateTo = $dateTo;
}
/**
* @return int
*/
public function getPersonsIncluded(): int
{
return $this->personsIncluded;
}
/**
* @param int $personsIncluded
*/
public function setPersonsIncluded(int $personsIncluded): void
{
$this->personsIncluded = $personsIncluded;
}
/**
* @return float
*/
public function getPrice(): float
{
return $this->price;
}
/**
* @param float $price
*/
public function setPrice(float $price): void
{
$this->price = $price;
@@ -138,9 +89,6 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
return $this->priceChf;
}
/**
* @param float $price
*/
public function setPriceChf(float $price): void
{
$this->priceChf = $price;
@@ -154,9 +102,6 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
return $this->priceAdditionalPerson;
}
/**
* @param float $priceAdditionalPerson
*/
public function setPriceAdditionalPerson(float $priceAdditionalPerson): void
{
$this->priceAdditionalPerson = $priceAdditionalPerson;
@@ -170,17 +115,11 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
return $this->priceAdditionalPersonChf;
}
/**
* @param float $priceAdditionalPerson
*/
public function setPriceAdditionalPersonChf(float $priceAdditionalPerson): void
{
$this->priceAdditionalPersonChf = $priceAdditionalPerson;
}
/**
* @return array
*/
public function jsonSerialize(): array
{
return [
@@ -4,31 +4,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* Copyright notice
*
* (c) 2020 Björn Fromme <[email protected]>, dreipunktnull
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
class GroupsPriceOption extends AbstractEntity implements \JsonSerializable
{
const TYPE_GLOBAL = 1;
@@ -61,33 +36,21 @@ class GroupsPriceOption extends AbstractEntity implements \JsonSerializable
*/
protected $ignoreWithBoard = false;
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* @return float
*/
public function getPrice(): float
{
return $this->price;
}
/**
* @param float $price
*/
public function setPrice(float $price): void
{
$this->price = $price;
@@ -101,49 +64,31 @@ class GroupsPriceOption extends AbstractEntity implements \JsonSerializable
return $this->priceChf;
}
/**
* @param float $price
*/
public function setPriceChf(float $price): void
{
$this->priceChf = $price;
}
/**
* @return int
*/
public function getType(): int
{
return $this->type;
}
/**
* @param int $type
*/
public function setType(int $type): void
{
$this->type = $type;
}
/**
* @return bool
*/
public function isIgnoreWithBoard(): bool
{
return $this->ignoreWithBoard;
}
/**
* @param bool $ignoreWithBoard
*/
public function setIgnoreWithBoard(bool $ignoreWithBoard): void
{
$this->ignoreWithBoard = $ignoreWithBoard;
}
/**
* @return array
*/
public function jsonSerialize(): array
{
return [
@@ -2,30 +2,6 @@
namespace EP\EpProducts\Domain\Model;
/***************************************************************
*
* 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 TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
use EP\EpProducts\Utility\HotelIconUtility;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
@@ -367,7 +343,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param bool $hidden
*/
public function setHidden($hidden)
public function setHidden($hidden): void
{
$this->hidden = $hidden;
}
@@ -383,7 +359,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param bool $earlybird
*/
public function setEarlybird($earlybird)
public function setEarlybird($earlybird): void
{
$this->earlybird = $earlybird;
}
@@ -399,7 +375,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param bool $new
*/
public function setNew($new)
public function setNew($new): void
{
$this->new = $new;
}
@@ -415,7 +391,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param bool $lowContingent
*/
public function setLowContingent($lowContingent)
public function setLowContingent($lowContingent): void
{
$this->lowContingent = $lowContingent;
}
@@ -431,7 +407,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param bool $showAsTeaser
*/
public function setShowAsTeaser($showAsTeaser)
public function setShowAsTeaser($showAsTeaser): void
{
$this->showAsTeaser = $showAsTeaser;
}
@@ -447,7 +423,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $detailPage
*/
public function setDetailPage($detailPage)
public function setDetailPage($detailPage): void
{
$this->detailPage = $detailPage;
}
@@ -463,7 +439,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -483,7 +459,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $shortName
*/
public function setShortName($shortName)
public function setShortName($shortName): void
{
$this->shortName = $shortName;
}
@@ -499,7 +475,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $keywords
*/
public function setKeywords($keywords)
public function setKeywords($keywords): void
{
$this->keywords = $keywords;
}
@@ -514,7 +490,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $headline
*/
public function setHeadline($headline) {
public function setHeadline($headline): void {
$this->headline = $headline;
}
@@ -528,7 +504,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $teaser
*/
public function setTeaser($teaser) {
public function setTeaser($teaser): void {
$this->teaser = $teaser;
}
@@ -543,7 +519,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $headerTitle
*/
public function setHeaderTitle($headerTitle)
public function setHeaderTitle($headerTitle): void
{
$this->headerTitle = $headerTitle;
}
@@ -559,7 +535,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $headerSubtitle
*/
public function setHeaderSubtitle($headerSubtitle)
public function setHeaderSubtitle($headerSubtitle): void
{
$this->headerSubtitle = $headerSubtitle;
}
@@ -575,7 +551,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $description
*/
public function setDescription($description)
public function setDescription($description): void
{
$this->description = $description;
}
@@ -590,7 +566,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $features
*/
public function setFeatures($features) {
public function setFeatures($features): void {
$this->features = $features;
}
@@ -604,7 +580,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $roomTypes
*/
public function setRoomTypes($roomTypes) {
public function setRoomTypes($roomTypes): void {
$this->roomTypes = $roomTypes;
}
@@ -618,7 +594,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $additionalInformation
*/
public function setAdditionalInformation($additionalInformation) {
public function setAdditionalInformation($additionalInformation): void {
$this->additionalInformation = $additionalInformation;
}
@@ -637,7 +613,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param float $latitude
*/
public function setLatitude($latitude)
public function setLatitude($latitude): void
{
$this->latitude = $latitude;
}
@@ -657,7 +633,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param float $longitude
*/
public function setLongitude($longitude)
public function setLongitude($longitude): void
{
$this->longitude = $longitude;
}
@@ -673,7 +649,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $address
*/
public function setAddress($address)
public function setAddress($address): void
{
$this->address = $address;
}
@@ -686,10 +662,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->images;
}
/**
* @param ObjectStorage $images
*/
public function setImages(ObjectStorage $images)
public function setImages(ObjectStorage $images): void
{
$this->images = $images;
}
@@ -705,7 +678,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $resellerImages
*/
public function setResellerImages($resellerImages)
public function setResellerImages($resellerImages): void
{
$this->resellerImages = $resellerImages;
}
@@ -721,7 +694,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @return null|ObjectStorage
*/
public function getTeaserImage()
public function getTeaserImage(): null|FileReference|false
{
if ($this->teaserImages->count() === 0) {
return null;
@@ -735,7 +708,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $teaserImages
*/
public function setTeaserImages($teaserImages)
public function setTeaserImages($teaserImages): void
{
$this->teaserImages = $teaserImages;
}
@@ -748,7 +721,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
$headerImages = $this->headerImages;
if ($headerImages->count() === 0 && null !== $this->getCity()) {
$headerImages = $this->getCity()->getHeaderImages();
return $this->getCity()->getHeaderImages();
}
return $headerImages;
@@ -757,7 +730,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @return null|ObjectStorage
*/
public function getHeaderImage()
public function getHeaderImage(): null|FileReference|false
{
if ($this->headerImages->count() === 0) {
return null;
@@ -771,7 +744,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $headerImages
*/
public function setHeaderImages($headerImages)
public function setHeaderImages($headerImages): void
{
$this->headerImages = $headerImages;
}
@@ -787,23 +760,17 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
/**
* @param MatchCode $code
*/
public function addAdditionalCode(MatchCode $code)
public function addAdditionalCode(MatchCode $code): void
{
$this->additionalCodes->attach($code);
}
/**
* @param MatchCode $codeToRemove
*/
public function removeAdditionalCodes(MatchCode $codeToRemove)
public function removeAdditionalCodes(MatchCode $codeToRemove): void
{
$this->additionalCodes->detach($codeToRemove);
}
@@ -816,10 +783,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->additionalCodes;
}
/**
* @param ObjectStorage $codes
*/
public function setAdditionalCodes(ObjectStorage $codes)
public function setAdditionalCodes(ObjectStorage $codes): void
{
$this->additionalCodes = $codes;
}
@@ -835,7 +799,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param int $type
*/
public function setType($type)
public function setType($type): void
{
$this->type = $type;
}
@@ -863,7 +827,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param int $category
*/
public function setCategory($category)
public function setCategory($category): void
{
$this->category = $category;
}
@@ -876,10 +840,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->country;
}
/**
* @param Country $country
*/
public function setCountry(Country $country)
public function setCountry(Country $country): void
{
$this->country = $country;
}
@@ -892,10 +853,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->region;
}
/**
* @param Region $region
*/
public function setRegion(Region $region)
public function setRegion(Region $region): void
{
$this->region = $region;
}
@@ -908,18 +866,12 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->city;
}
/**
* @param City $city
*/
public function setCity(City $city)
public function setCity(City $city): void
{
$this->city = $city;
}
/**
* @param Teammember $teamer
*/
public function addTeamer(Teammember $teamer)
public function addTeamer(Teammember $teamer): void
{
$this->teamer->attach($teamer);
}
@@ -927,7 +879,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param Teammember $teamerToRemove The Teammember to be removed
*/
public function removeTeamer(Teammember $teamerToRemove)
public function removeTeamer(Teammember $teamerToRemove): void
{
$this->teamer->detach($teamerToRemove);
}
@@ -940,26 +892,17 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->teamer;
}
/**
* @param ObjectStorage $teamer
*/
public function setTeamer(ObjectStorage $teamer)
public function setTeamer(ObjectStorage $teamer): void
{
$this->teamer = $teamer;
}
/**
* @param Product $product
*/
public function addProduct(Product $product)
public function addProduct(Product $product): void
{
$this->products->attach($product);
}
/**
* @param Product $product
*/
public function removeProduct(Product $product)
public function removeProduct(Product $product): void
{
$this->products->detach($product);
}
@@ -972,33 +915,17 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->products;
}
/**
* @param ObjectStorage $products
*/
public function setProducts(ObjectStorage $products)
public function setProducts(ObjectStorage $products): void
{
$this->products = $products;
}
/**
* @param ObjectStorage $rooms
*/
public function setRooms($rooms) {
$this->rooms = $rooms;
}
/**
* @param Fact $fact
*/
public function addFact(Fact $fact)
public function addFact(Fact $fact): void
{
$this->facts->attach($fact);
}
/**
* @param Fact $factToRemove
*/
public function removeFact(Fact $factToRemove)
public function removeFact(Fact $factToRemove): void
{
$this->facts->detach($factToRemove);
}
@@ -1011,10 +938,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->facts;
}
/**
* @param ObjectStorage $facts
*/
public function setFacts(ObjectStorage $facts)
public function setFacts(ObjectStorage $facts): void
{
$this->facts = $facts;
}
@@ -1038,31 +962,22 @@ class Hotel extends AbstractEntity implements TeaserInterface
/**
* @param string $externalLink
*/
public function setExternalLink($externalLink)
public function setExternalLink($externalLink): void
{
$this->externalLink = $externalLink;
}
/**
* @return bool
*/
public function getIsApartment()
public function getIsApartment(): bool
{
return $this->getType() === self::TYPE_APPARTMENT;
}
/**
* @return bool
*/
public function getIsHolidayFlat()
public function getIsHolidayFlat(): bool
{
return $this->getType() === self::TYPE_HOLIDAY_FLAT;
}
/**
* @return bool
*/
public function getIsOther()
public function getIsOther(): bool
{
return !$this->getIsApartment() && !$this->getIsHolidayFlat();
}
@@ -1075,10 +990,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->groupsPriceConfigs;
}
/**
* @param ObjectStorage $groupPriceConfigs
*/
public function setGroupsPriceConfigs(ObjectStorage $groupPriceConfigs)
public function setGroupsPriceConfigs(ObjectStorage $groupPriceConfigs): void
{
$this->groupsPriceConfigs = $groupPriceConfigs;
}
@@ -1091,10 +1003,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->groupsPriceOptions;
}
/**
* @param ObjectStorage $groupsPriceOptions
*/
public function setGroupsPriceOptions(ObjectStorage $groupsPriceOptions)
public function setGroupsPriceOptions(ObjectStorage $groupsPriceOptions): void
{
$this->groupsPriceOptions = $groupsPriceOptions;
}
@@ -1107,10 +1016,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->groupsPriceBoards;
}
/**
* @param ObjectStorage $groupsPriceBoards
*/
public function setGroupsPriceBoards(ObjectStorage $groupsPriceBoards)
public function setGroupsPriceBoards(ObjectStorage $groupsPriceBoards): void
{
$this->groupsPriceBoards = $groupsPriceBoards;
}
@@ -1120,7 +1026,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->groupsPriceAdolescentsAge;
}
public function setGroupsPriceAdolescentsAge($groupsPriceAdolescentsAge)
public function setGroupsPriceAdolescentsAge($groupsPriceAdolescentsAge): void
{
$this->groupsPriceAdolescentsAge = $groupsPriceAdolescentsAge;
}
@@ -1130,7 +1036,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->groupsPriceTaxLabel;
}
public function setGroupsPriceTaxLabel($groupsPriceTaxLabel)
public function setGroupsPriceTaxLabel($groupsPriceTaxLabel): void
{
$this->groupsPriceTaxLabel = $groupsPriceTaxLabel;
}
@@ -1140,7 +1046,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->mainSeasonFrom;
}
public function setMainSeasonFrom(\DateTime $mainSeasonFrom)
public function setMainSeasonFrom(\DateTime $mainSeasonFrom): void
{
$this->mainSeasonFrom = $mainSeasonFrom;
}
@@ -1150,7 +1056,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->mainSeasonTo;
}
public function setMainSeasonTo(\DateTime $mainSeasonTo)
public function setMainSeasonTo(\DateTime $mainSeasonTo): void
{
$this->mainSeasonTo = $mainSeasonTo;
}
@@ -1160,7 +1066,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->bedsCount;
}
public function setBedsCount($bedsCount)
public function setBedsCount($bedsCount): void
{
$this->bedsCount = $bedsCount;
}
@@ -1170,7 +1076,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->parkingCount;
}
public function setParkingCount($parkingCount)
public function setParkingCount($parkingCount): void
{
$this->parkingCount = $parkingCount;
}
@@ -1180,7 +1086,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->altitude;
}
public function setAltitude($altitude)
public function setAltitude($altitude): void
{
$this->altitude = $altitude;
}
@@ -1190,7 +1096,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->shower;
}
public function setShower($shower)
public function setShower($shower): void
{
$this->shower = $shower;
}
@@ -1200,7 +1106,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->partyroom;
}
public function setPartyroom($partyroom)
public function setPartyroom($partyroom): void
{
$this->partyroom = $partyroom;
}
@@ -1210,7 +1116,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->gamesroom;
}
public function setGamesroom($gamesroom)
public function setGamesroom($gamesroom): void
{
$this->gamesroom = $gamesroom;
}
@@ -1220,7 +1126,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->sauna;
}
public function setSauna($sauna)
public function setSauna($sauna): void
{
$this->sauna = $sauna;
}
@@ -1230,7 +1136,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->wifi;
}
public function setWifi($wifi)
public function setWifi($wifi): void
{
$this->wifi = $wifi;
}
@@ -1240,7 +1146,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->distanceBus;
}
public function setDistanceBus($distanceBus)
public function setDistanceBus($distanceBus): void
{
$this->distanceBus = $distanceBus;
}
@@ -1250,7 +1156,7 @@ class Hotel extends AbstractEntity implements TeaserInterface
return $this->distanceLift;
}
public function setDistanceLift($distanceLift)
public function setDistanceLift($distanceLift): void
{
$this->distanceLift = $distanceLift;
}
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
@@ -33,7 +35,7 @@ class Infobox extends AbstractEntity
/**
* @param string $title
*/
public function setTitle($title)
public function setTitle($title): void
{
$this->title = $title;
}
@@ -49,7 +51,7 @@ class Infobox extends AbstractEntity
/**
* @param string $text
*/
public function setText($text)
public function setText($text): void
{
$this->text = $text;
}
@@ -65,9 +67,8 @@ class Infobox extends AbstractEntity
/**
* @param bool $defaultText
*/
public function setDefaultText($defaultText)
public function setDefaultText($defaultText): void
{
$this->defaultText = $defaultText;
}
}
@@ -1,34 +1,12 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/***************************************************************
*
* 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 Journey extends AbstractEntity
{
@@ -63,7 +41,7 @@ class Journey extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -79,7 +57,7 @@ class Journey extends AbstractEntity
/**
* @param string $byCar
*/
public function setByCar($byCar)
public function setByCar($byCar): void
{
$this->byCar = $byCar;
}
@@ -95,7 +73,7 @@ class Journey extends AbstractEntity
/**
* @param string $byBus
*/
public function setByBus($byBus)
public function setByBus($byBus): void
{
$this->byBus = $byBus;
}
@@ -111,7 +89,7 @@ class Journey extends AbstractEntity
/**
* @param FileReference $image
*/
public function setImage($image)
public function setImage($image): void
{
$this->image = $image;
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 MatchCode extends AbstractEntity
{
@@ -47,9 +25,9 @@ class MatchCode extends AbstractEntity
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
}
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Officetip extends AbstractEntity
{
@@ -57,7 +35,7 @@ class Officetip extends AbstractEntity
/**
* @param string $label
*/
public function setLabel($label)
public function setLabel($label): void
{
$this->label = $label;
}
@@ -73,7 +51,7 @@ class Officetip extends AbstractEntity
/**
* @param string $text
*/
public function setText($text)
public function setText($text): void
{
$this->text = $text;
}
@@ -86,10 +64,7 @@ class Officetip extends AbstractEntity
return $this->author;
}
/**
* @param Teammember $author
*/
public function setAuthor(Teammember $author)
public function setAuthor(Teammember $author): void
{
$this->author = $author;
}
@@ -6,31 +6,6 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/***************************************************************
*
* 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 Product extends AbstractEntity implements TeaserInterface
{
@@ -304,6 +279,11 @@ class Product extends AbstractEntity implements TeaserInterface
*/
protected $nonBookableInfo;
/**
* @var string
*/
protected $popupText = '';
public function __construct()
{
$this->initStorageObjects();
@@ -330,15 +310,12 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param bool $searchable
*/
public function setSearchable($searchable)
public function setSearchable($searchable): void
{
$this->searchable = (bool) $searchable;
}
/**
* @return boolean
*/
public function getBookable()
public function getBookable(): bool
{
return (bool) $this->bookable;
}
@@ -346,7 +323,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param boolean $bookable
*/
public function setBookable($bookable)
public function setBookable($bookable): void
{
$this->bookable = (bool) $bookable;
}
@@ -362,15 +339,12 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param bool $hideBookingButton
*/
public function setHideBookingButton($hideBookingButton)
public function setHideBookingButton($hideBookingButton): void
{
$this->hideBookingButton = (bool) $hideBookingButton;
}
/**
* @return bool
*/
public function isDaytrip()
public function isDaytrip(): bool
{
return (bool) $this->daytrip;
}
@@ -378,7 +352,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param bool $daytrip
*/
public function setDaytrip($daytrip)
public function setDaytrip($daytrip): void
{
$this->daytrip = (bool) $daytrip;
}
@@ -394,7 +368,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param bool $important
*/
public function setImportant($important)
public function setImportant($important): void
{
$this->important = $important;
}
@@ -410,7 +384,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param bool $skipassIncluded
*/
public function setSkipassIncluded($skipassIncluded)
public function setSkipassIncluded($skipassIncluded): void
{
$this->skipassIncluded = $skipassIncluded;
}
@@ -425,7 +399,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $detailPage
*/
public function setDetailPage($detailPage) {
public function setDetailPage($detailPage): void {
$this->detailPage = $detailPage;
}
@@ -440,7 +414,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $externalLink
*/
public function setExternalLink($externalLink)
public function setExternalLink($externalLink): void
{
$this->externalLink = $externalLink;
}
@@ -456,7 +430,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -472,7 +446,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $nameInternal
*/
public function setNameInternal($nameInternal)
public function setNameInternal($nameInternal): void
{
$this->nameInternal = $nameInternal;
}
@@ -488,7 +462,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $keywords
*/
public function setKeywords($keywords)
public function setKeywords($keywords): void
{
$this->keywords = $keywords;
}
@@ -503,7 +477,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $headline
*/
public function setHeadline($headline) {
public function setHeadline($headline): void {
$this->headline = $headline;
}
@@ -518,7 +492,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param int $busProId
*/
public function setBusProId($busProId)
public function setBusProId($busProId): void
{
$this->busProId = $busProId;
}
@@ -534,7 +508,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
@@ -550,7 +524,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $season
*/
public function setSeason($season)
public function setSeason($season): void
{
$this->season = $season;
}
@@ -566,7 +540,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $feature
*/
public function setFeature($feature)
public function setFeature($feature): void
{
$this->feature = $feature;
}
@@ -581,7 +555,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $teaser
*/
public function setTeaser($teaser) {
public function setTeaser($teaser): void {
$this->teaser = $teaser;
}
@@ -596,7 +570,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $teaserLong
*/
public function setTeaserLong($teaserLong)
public function setTeaserLong($teaserLong): void
{
$this->teaserLong = $teaserLong;
}
@@ -612,7 +586,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $headerTitle
*/
public function setHeaderTitle($headerTitle)
public function setHeaderTitle($headerTitle): void
{
$this->headerTitle = $headerTitle;
}
@@ -628,7 +602,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $headerSubtitle
*/
public function setHeaderSubtitle($headerSubtitle)
public function setHeaderSubtitle($headerSubtitle): void
{
$this->headerSubtitle = $headerSubtitle;
}
@@ -643,7 +617,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $conceptDescription
*/
public function setConceptDescription($conceptDescription) {
public function setConceptDescription($conceptDescription): void {
$this->conceptDescription = $conceptDescription;
}
@@ -657,7 +631,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $boardDescription
*/
public function setBoardDescription($boardDescription) {
public function setBoardDescription($boardDescription): void {
$this->boardDescription = $boardDescription;
}
@@ -671,7 +645,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $additionalServices
*/
public function setAdditionalServices($additionalServices) {
public function setAdditionalServices($additionalServices): void {
$this->additionalServices = $additionalServices;
}
@@ -686,7 +660,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $includedServices
*/
public function setIncludedServices($includedServices)
public function setIncludedServices($includedServices): void
{
$this->includedServices = $includedServices;
}
@@ -701,7 +675,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $programmeDescription
*/
public function setProgrammeDescription($programmeDescription) {
public function setProgrammeDescription($programmeDescription): void {
$this->programmeDescription = $programmeDescription;
}
@@ -716,7 +690,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $skiPassAltTitle
*/
public function setSkiPassAltTitle($skiPassAltTitle)
public function setSkiPassAltTitle($skiPassAltTitle): void
{
$this->skiPassAltTitle = $skiPassAltTitle;
}
@@ -731,7 +705,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $skiPassDescription
*/
public function setSkiPassDescription($skiPassDescription) {
public function setSkiPassDescription($skiPassDescription): void {
$this->skiPassDescription = $skiPassDescription;
}
@@ -746,7 +720,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param float $ratingAverage
*/
public function setRatingAverage($ratingAverage)
public function setRatingAverage($ratingAverage): void
{
$this->ratingAverage = $ratingAverage;
}
@@ -762,7 +736,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param int $ratingVotesCount
*/
public function setRatingVotesCount($ratingVotesCount)
public function setRatingVotesCount($ratingVotesCount): void
{
$this->ratingVotesCount = $ratingVotesCount;
}
@@ -778,7 +752,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $testimonials
*/
public function setTestimonials($testimonials)
public function setTestimonials($testimonials): void
{
$this->testimonials = $testimonials;
}
@@ -794,7 +768,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<FileReference> $images
*/
public function setImages(ObjectStorage $images)
public function setImages(ObjectStorage $images): void
{
$this->images = $images;
}
@@ -814,7 +788,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<FileReference> $images
*/
public function setResellerImages(ObjectStorage $images)
public function setResellerImages(ObjectStorage $images): void
{
$this->resellerImages = $images;
}
@@ -845,7 +819,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $teaserImages
*/
public function setTeaserImages($teaserImages)
public function setTeaserImages($teaserImages): void
{
$this->teaserImages = $teaserImages;
}
@@ -861,7 +835,7 @@ class Product extends AbstractEntity implements TeaserInterface
$headerImages = $this->headerImages;
if ($headerImages->count() === 0 && null !== $this->getRegion()) {
$headerImages = $this->getRegion()->getHeaderImages();
return $this->getRegion()->getHeaderImages();
}
return $headerImages;
@@ -888,7 +862,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage $headerImages
*/
public function setHeaderImages($headerImages)
public function setHeaderImages($headerImages): void
{
$this->headerImages = $headerImages;
}
@@ -901,10 +875,7 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->concept;
}
/**
* @param Concept $concept
*/
public function setConcepts(Concept $concept)
public function setConcepts(Concept $concept): void
{
$this->concept = $concept;
}
@@ -917,10 +888,7 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->country;
}
/**
* @param Country $country
*/
public function setCountry(Country $country)
public function setCountry(Country $country): void
{
$this->country = $country;
}
@@ -933,10 +901,7 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->region;
}
/**
* @param Region $region
*/
public function setRegion(Region $region)
public function setRegion(Region $region): void
{
$this->region = $region;
}
@@ -949,10 +914,7 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->city;
}
/**
* @param City $city
*/
public function setCity(City $city)
public function setCity(City $city): void
{
$this->city = $city;
}
@@ -968,7 +930,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Journey $journey
*/
public function setJourney($journey)
public function setJourney($journey): void
{
$this->journey = $journey;
}
@@ -984,23 +946,17 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<Fact> $facts
*/
public function setFacts(ObjectStorage $facts)
public function setFacts(ObjectStorage $facts): void
{
$this->facts = $facts;
}
/**
* @param Hotel $hotel
*/
public function addHotel(Hotel $hotel)
public function addHotel(Hotel $hotel): void
{
$this->hotels->attach($hotel);
}
/**
* @param Hotel $hotel
*/
public function removeHotel(Hotel $hotel)
public function removeHotel(Hotel $hotel): void
{
$this->hotels->detach($hotel);
}
@@ -1016,7 +972,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<Hotel> $hotels
*/
public function setHotels(ObjectStorage $hotels)
public function setHotels(ObjectStorage $hotels): void
{
$this->hotels = $hotels;
}
@@ -1031,7 +987,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param ObjectStorage<Faq> $faq
*/
public function setFaqs(ObjectStorage $faqs)
public function setFaqs(ObjectStorage $faqs): void
{
$this->faqs = $faqs;
}
@@ -1047,7 +1003,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Officetip $officetip
*/
public function setOfficetip($officetip)
public function setOfficetip($officetip): void
{
$this->officetip = $officetip;
}
@@ -1063,7 +1019,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Hotel $calendarHotel
*/
public function setCalendarHotel($calendarHotel)
public function setCalendarHotel($calendarHotel): void
{
$this->calendarHotel = $calendarHotel;
}
@@ -1079,7 +1035,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Product $altProduct
*/
public function setAltProduct($altProduct)
public function setAltProduct($altProduct): void
{
$this->altProduct = $altProduct;
}
@@ -1095,7 +1051,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $altLabel
*/
public function setAltLabel($altLabel)
public function setAltLabel($altLabel): void
{
$this->altLabel = $altLabel;
}
@@ -1111,7 +1067,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param string $video
*/
public function setVideo($video)
public function setVideo($video): void
{
$this->video = $video;
}
@@ -1127,7 +1083,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param FileReference $banner
*/
public function setBanner($banner)
public function setBanner($banner): void
{
$this->banner = $banner;
}
@@ -1143,7 +1099,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Badge $badge
*/
public function setBadge($badge)
public function setBadge($badge): void
{
$this->badge = $badge;
}
@@ -1156,9 +1112,6 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->nonBookableDates;
}
/**
* @return int
*/
public function getMinPrice(): int
{
return $this->minPrice;
@@ -1167,7 +1120,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param int $minPrice
*/
public function setMinPrice($minPrice)
public function setMinPrice($minPrice): void
{
$this->minPrice = $minPrice;
}
@@ -1183,7 +1136,7 @@ class Product extends AbstractEntity implements TeaserInterface
/**
* @param Infobox $nonBookableInfo
*/
public function setNonBookableInfo($nonBookableInfo)
public function setNonBookableInfo($nonBookableInfo): void
{
$this->nonBookableInfo = $nonBookableInfo;
}
@@ -1193,17 +1146,29 @@ class Product extends AbstractEntity implements TeaserInterface
return $this->tourThumbnail;
}
public function setTourThumbnail(FileReference $tourThumbnail)
public function setTourThumbnail(FileReference $tourThumbnail): void
{
$this->tourThumbnail = $tourThumbnail;
}
public function getPopupText()
{
return $this->popupText;
}
public function setPopupText($popupText): static
{
$this->popupText = $popupText;
return $this;
}
public function getTourLink()
{
return $this->tourLink;
}
public function setTourLink(string $tourLink)
public function setTourLink(string $tourLink): void
{
$this->tourLink = $tourLink;
}
@@ -6,30 +6,6 @@ use TYPO3\CMS\Extbase\Annotation\ORM\Cascade;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/***************************************************************
*
* 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 Region extends AbstractDestination implements TeaserInterface
{
@@ -157,7 +133,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param bool $excludeFromTeasergroups
*/
public function setExcludeFromTeasergroups($excludeFromTeasergroups)
public function setExcludeFromTeasergroups($excludeFromTeasergroups): void
{
$this->excludeFromTeasergroups = (bool) $excludeFromTeasergroups;
}
@@ -172,7 +148,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $skiArea
*/
public function setSkiArea($skiArea) {
public function setSkiArea($skiArea): void {
$this->skiArea = $skiArea;
}
@@ -187,7 +163,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $skiAreaExtended
*/
public function setSkiAreaExtended($skiAreaExtended)
public function setSkiAreaExtended($skiAreaExtended): void
{
$this->skiAreaExtended = $skiAreaExtended;
}
@@ -202,7 +178,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $skiPass
*/
public function setSkiPass($skiPass) {
public function setSkiPass($skiPass): void {
$this->skiPass = $skiPass;
}
@@ -216,7 +192,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $news
*/
public function setNews($news) {
public function setNews($news): void {
$this->news = $news;
}
@@ -230,7 +206,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $length
*/
public function setLength($length) {
public function setLength($length): void {
$this->length = $length;
}
@@ -244,7 +220,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $height
*/
public function setHeight($height) {
public function setHeight($height): void {
$this->height = $height;
}
@@ -258,7 +234,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $lifts
*/
public function setLifts($lifts) {
public function setLifts($lifts): void {
$this->lifts = $lifts;
}
@@ -273,7 +249,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $feature
*/
public function setFeature($feature)
public function setFeature($feature): void
{
$this->feature = $feature;
}
@@ -289,7 +265,7 @@ class Region extends AbstractDestination implements TeaserInterface
$headerImages = $this->headerImages;
if ($headerImages->count() === 0 && null !== $this->getCountry()) {
$headerImages = $this->getCountry()->getHeaderImages();
return $this->getCountry()->getHeaderImages();
}
return $headerImages;
@@ -303,10 +279,7 @@ class Region extends AbstractDestination implements TeaserInterface
return $this->country;
}
/**
* @param Country $country
*/
public function setCountry(Country $country)
public function setCountry(Country $country): void
{
$this->country = $country;
}
@@ -321,7 +294,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param ObjectStorage $cities
*/
public function setCities($cities) {
public function setCities($cities): void {
$this->cities = $cities;
}
@@ -335,7 +308,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param ObjectStorage $products
*/
public function setProducts($products) {
public function setProducts($products): void {
$this->products = $products;
}
@@ -352,7 +325,7 @@ class Region extends AbstractDestination implements TeaserInterface
*
* @param ObjectStorage<Fact> $facts
*/
public function setFacts(ObjectStorage $facts)
public function setFacts(ObjectStorage $facts): void
{
$this->facts = $facts;
}
@@ -367,7 +340,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param ObjectStorage $hotels
*/
public function setHotels($hotels) {
public function setHotels($hotels): void {
$this->hotels = $hotels;
}
@@ -382,7 +355,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param Snowreport $snowreport
*/
public function setSnowreport($snowreport)
public function setSnowreport($snowreport): void
{
$this->snowreport = $snowreport;
}
@@ -395,10 +368,7 @@ class Region extends AbstractDestination implements TeaserInterface
return $this->webcam;
}
/**
* @return bool
*/
public function getWebcamAvailable()
public function getWebcamAvailable(): bool
{
$webcam = $this->getWebcam();
return $webcam !== null && $webcam->getAvailable();
@@ -407,7 +377,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param Webcam $webcam
*/
public function setWebcam($webcam)
public function setWebcam($webcam): void
{
$this->webcam = $webcam;
}
@@ -423,7 +393,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
*/
public function setRegionMaps($regionMaps)
public function setRegionMaps($regionMaps): void
{
$this->regionMaps = $regionMaps;
}
@@ -439,7 +409,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param FileReference $awardImage
*/
public function setAwardImage($awardImage)
public function setAwardImage($awardImage): void
{
$this->awardImage = $awardImage;
}
@@ -455,7 +425,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $awardLink
*/
public function setAwardLink($awardLink)
public function setAwardLink($awardLink): void
{
$this->awardLink = $awardLink;
}
@@ -471,7 +441,7 @@ class Region extends AbstractDestination implements TeaserInterface
/**
* @param string $awardLabel
*/
public function setAwardLabel($awardLabel)
public function setAwardLabel($awardLabel): void
{
$this->awardLabel = $awardLabel;
}
@@ -6,31 +6,6 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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 Reseller extends AbstractEntity
{
@@ -71,7 +46,7 @@ class Reseller extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -87,7 +62,7 @@ class Reseller extends AbstractEntity
/**
* @param string $code
*/
public function setCode($code)
public function setCode($code): void
{
$this->code = $code;
}
@@ -103,7 +78,7 @@ class Reseller extends AbstractEntity
/**
* @param string $paCode
*/
public function setPaCode($paCode)
public function setPaCode($paCode): void
{
$this->paCode = $paCode;
}
@@ -119,7 +94,7 @@ class Reseller extends AbstractEntity
/**
* @param ObjectStorage $products
*/
public function setProducts($products)
public function setProducts($products): void
{
$this->products = $products;
}
@@ -1,31 +1,8 @@
<?php
namespace EP\EpProducts\Domain\Model;
declare(strict_types=1);
/***************************************************************
*
* 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!
***************************************************************/
namespace EP\EpProducts\Domain\Model;
class Room
{
@@ -1,53 +1,17 @@
<?php
namespace EP\EpProducts\Domain\Model;
declare(strict_types=1);
/***************************************************************
*
* 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!
***************************************************************/
namespace EP\EpProducts\Domain\Model;
class SearchResult
{
/**
* @var int
*/
protected $total = 0;
/**
* @var array
*/
protected $data = [];
/**
* @param array $data
* @param int $total
*/
public function __construct(array $data = [], $total = 0)
public function __construct(private array $data = [], private $total = 0)
{
$this->data = $data;
$this->total = $total;
}
/**
@@ -61,23 +25,17 @@ class SearchResult
/**
* @param int $total
*/
public function setTotal($total)
public function setTotal($total): void
{
$this->total = $total;
}
/**
* @return array
*/
public function getData()
public function getData(): array
{
return $this->data;
}
/**
* @param array $data
*/
public function setData($data)
public function setData(array $data): void
{
$this->data = $data;
}
@@ -5,31 +5,6 @@ namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* 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 Snowreport extends AbstractEntity
{
@@ -129,7 +104,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $vendorUid
*/
public function setVendorUid($vendorUid)
public function setVendorUid($vendorUid): void
{
$this->vendorUid = $vendorUid;
}
@@ -145,7 +120,7 @@ class Snowreport extends AbstractEntity
/**
* @param string $regionName
*/
public function setRegionName($regionName)
public function setRegionName($regionName): void
{
$this->regionName = $regionName;
}
@@ -161,7 +136,7 @@ class Snowreport extends AbstractEntity
/**
* @param bool $glacier
*/
public function setGlacier($glacier)
public function setGlacier($glacier): void
{
$this->glacier = $glacier;
}
@@ -177,7 +152,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $heightMax
*/
public function setHeightMax($heightMax)
public function setHeightMax($heightMax): void
{
$this->heightMax = $heightMax;
}
@@ -193,7 +168,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $heightMin
*/
public function setHeightMin($heightMin)
public function setHeightMin($heightMin): void
{
$this->heightMin = $heightMin;
}
@@ -209,7 +184,7 @@ class Snowreport extends AbstractEntity
/**
* @param bool $opened
*/
public function setOpened($opened)
public function setOpened($opened): void
{
$this->opened = $opened;
}
@@ -225,7 +200,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $snowLevelMountain
*/
public function setSnowLevelMountain($snowLevelMountain)
public function setSnowLevelMountain($snowLevelMountain): void
{
$this->snowLevelMountain = $snowLevelMountain;
}
@@ -241,7 +216,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $snowLevelValley
*/
public function setSnowLevelValley($snowLevelValley)
public function setSnowLevelValley($snowLevelValley): void
{
$this->snowLevelValley = $snowLevelValley;
}
@@ -257,7 +232,7 @@ class Snowreport extends AbstractEntity
/**
* @param string $snowQuality
*/
public function setSnowQuality($snowQuality)
public function setSnowQuality($snowQuality): void
{
$this->snowQuality = $snowQuality;
}
@@ -273,15 +248,12 @@ class Snowreport extends AbstractEntity
/**
* @param \DateTime $lastSnowfall
*/
public function setLastSnowfall($lastSnowfall)
public function setLastSnowfall($lastSnowfall): void
{
$this->lastSnowfall = $lastSnowfall;
}
/**
* @return int
*/
public function getLiftsTotal()
public function getLiftsTotal(): int
{
return (int) $this->liftsTotal;
}
@@ -289,15 +261,12 @@ class Snowreport extends AbstractEntity
/**
* @param int $liftsTotal
*/
public function setLiftsTotal($liftsTotal)
public function setLiftsTotal($liftsTotal): void
{
$this->liftsTotal = $liftsTotal;
}
/**
* @return int
*/
public function getLiftsOpened()
public function getLiftsOpened(): int
{
return (int) $this->liftsOpened;
}
@@ -305,7 +274,7 @@ class Snowreport extends AbstractEntity
/**
* @param int $liftsOpened
*/
public function setLiftsOpened($liftsOpened)
public function setLiftsOpened($liftsOpened): void
{
$this->liftsOpened = $liftsOpened;
}
@@ -321,23 +290,17 @@ class Snowreport extends AbstractEntity
/**
* @param \DateTime $lastUpdate
*/
public function setLastUpdate($lastUpdate)
public function setLastUpdate($lastUpdate): void
{
$this->lastUpdate = $lastUpdate;
}
/**
* @param Webcam $webcam
*/
public function addWebcam(Webcam $webcam)
public function addWebcam(Webcam $webcam): void
{
$this->webcams->attach($webcam);
}
/**
* @param Webcam $webcam
*/
public function removeWebcam(Webcam $webcam)
public function removeWebcam(Webcam $webcam): void
{
$this->webcams->detach($webcam);
}
@@ -353,7 +316,7 @@ class Snowreport extends AbstractEntity
/**
* @param ObjectStorage<Webcam> $webcams
*/
public function setWebcams(ObjectStorage $webcams)
public function setWebcams(ObjectStorage $webcams): void
{
$this->webcams = $webcams;
}
@@ -369,7 +332,7 @@ class Snowreport extends AbstractEntity
/**
* @param \DateTime $seasonStart
*/
public function setSeasonStart($seasonStart)
public function setSeasonStart($seasonStart): void
{
$this->seasonStart = $seasonStart;
}
@@ -385,7 +348,7 @@ class Snowreport extends AbstractEntity
/**
* @param \DateTime $seasonEnd
*/
public function setSeasonEnd($seasonEnd)
public function setSeasonEnd($seasonEnd): void
{
$this->seasonEnd = $seasonEnd;
}
@@ -7,31 +7,6 @@ use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Domain\Model\Category;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* 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 Teammember extends AbstractEntity
{
@@ -86,7 +61,7 @@ class Teammember extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -102,7 +77,7 @@ class Teammember extends AbstractEntity
/**
* @param string $email
*/
public function setEmail($email)
public function setEmail($email): void
{
$this->email = $email;
}
@@ -118,7 +93,7 @@ class Teammember extends AbstractEntity
/**
* @param string $phone
*/
public function setPhone($phone)
public function setPhone($phone): void
{
$this->phone = $phone;
}
@@ -134,7 +109,7 @@ class Teammember extends AbstractEntity
/**
* @param string $tasks
*/
public function setTasks($tasks)
public function setTasks($tasks): void
{
$this->tasks = $tasks;
}
@@ -150,7 +125,7 @@ class Teammember extends AbstractEntity
/**
* @param FileReference $image
*/
public function setImage($image)
public function setImage($image): void
{
$this->image = $image;
}
@@ -166,7 +141,7 @@ class Teammember extends AbstractEntity
/**
* @param string $description
*/
public function setDescription($description)
public function setDescription($description): void
{
$this->description = $description;
}
@@ -182,7 +157,7 @@ class Teammember extends AbstractEntity
/**
* @param ObjectStorage $categories
*/
public function setCategories($categories)
public function setCategories($categories): void
{
$this->categories = $categories;
}
@@ -1,34 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/***************************************************************
*
* 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!
***************************************************************/
interface TeaserInterface
{
@@ -121,7 +121,7 @@ class Travelinfo extends AbstractEntity
/**
* @param string $title
*/
public function setTitle($title)
public function setTitle($title): void
{
$this->title = $title;
}
@@ -131,7 +131,7 @@ class Travelinfo extends AbstractEntity
return $this->headerImage;
}
public function setHeaderImage($headerImage)
public function setHeaderImage($headerImage): static
{
$this->headerImage = $headerImage;
@@ -143,7 +143,7 @@ class Travelinfo extends AbstractEntity
return $this->footerImage;
}
public function setFooterImage($footerImage)
public function setFooterImage($footerImage): static
{
$this->footerImage = $footerImage;
@@ -155,7 +155,7 @@ class Travelinfo extends AbstractEntity
return $this->subline;
}
public function setSubline($subline)
public function setSubline($subline): static
{
$this->subline = $subline;
@@ -167,7 +167,7 @@ class Travelinfo extends AbstractEntity
return $this->headline;
}
public function setHeadline($headline)
public function setHeadline($headline): static
{
$this->headline = $headline;
@@ -179,7 +179,7 @@ class Travelinfo extends AbstractEntity
return $this->introText;
}
public function setIntroText($introText)
public function setIntroText($introText): static
{
$this->introText = $introText;
@@ -191,7 +191,7 @@ class Travelinfo extends AbstractEntity
return $this->busInfoText;
}
public function setBusInfoText($busInfoText)
public function setBusInfoText($busInfoText): static
{
$this->busInfoText = $busInfoText;
@@ -203,7 +203,7 @@ class Travelinfo extends AbstractEntity
return $this->departureText;
}
public function setDepartureText($departureText)
public function setDepartureText($departureText): static
{
$this->departureText = $departureText;
@@ -215,7 +215,7 @@ class Travelinfo extends AbstractEntity
return $this->footerText;
}
public function setFooterText($footerText)
public function setFooterText($footerText): static
{
$this->footerText = $footerText;
@@ -227,7 +227,7 @@ class Travelinfo extends AbstractEntity
return $this->additionalInfo;
}
public function setAdditionalInfo($additionalInfo)
public function setAdditionalInfo($additionalInfo): static
{
$this->additionalInfo = $additionalInfo;
@@ -239,7 +239,7 @@ class Travelinfo extends AbstractEntity
return $this->importantPhoneNumbers;
}
public function setImportantPhoneNumbers($importantPhoneNumbers)
public function setImportantPhoneNumbers($importantPhoneNumbers): static
{
$this->importantPhoneNumbers = $importantPhoneNumbers;
@@ -251,7 +251,7 @@ class Travelinfo extends AbstractEntity
return $this->importantLinks;
}
public function setImportantLinks($importantLinks)
public function setImportantLinks($importantLinks): static
{
$this->importantLinks = $importantLinks;
@@ -263,7 +263,7 @@ class Travelinfo extends AbstractEntity
return $this->selfArrangedText;
}
public function setSelfArrangedText($selfArrangedText)
public function setSelfArrangedText($selfArrangedText): static
{
$this->selfArrangedText = $selfArrangedText;
@@ -275,7 +275,7 @@ class Travelinfo extends AbstractEntity
return $this->teasers;
}
public function setTeasers($teasers)
public function setTeasers($teasers): void
{
$this->teasers = $teasers;
}
@@ -285,7 +285,7 @@ class Travelinfo extends AbstractEntity
return $this->links;
}
public function setLinks($links)
public function setLinks($links): static
{
$this->links = $links;
@@ -297,7 +297,7 @@ class Travelinfo extends AbstractEntity
return $this->travelCode;
}
public function setTravelCode($travelCode)
public function setTravelCode($travelCode): static
{
$this->travelCode = $travelCode;
@@ -309,7 +309,7 @@ class Travelinfo extends AbstractEntity
return $this->product;
}
public function setProduct($product)
public function setProduct($product): static
{
$this->product = $product;
return $this;
@@ -320,7 +320,7 @@ class Travelinfo extends AbstractEntity
return $this->hideAddress;
}
public function setHideAddress($hideAddress)
public function setHideAddress($hideAddress): static
{
$this->hideAddress = $hideAddress;
return $this;
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
@@ -32,7 +34,7 @@ class TravelinfoLink extends AbstractEntity
/**
* @param string $title
*/
public function setTitle($title)
public function setTitle($title): void
{
$this->title = $title;
}
@@ -42,7 +44,7 @@ class TravelinfoLink extends AbstractEntity
return $this->link;
}
public function setLink($link)
public function setLink($link): void
{
$this->link = $link;
}
@@ -52,9 +54,8 @@ class TravelinfoLink extends AbstractEntity
return $this->icon;
}
public function setIcon($icon)
public function setIcon($icon): void
{
$this->icon = $icon;
}
}
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
@@ -38,7 +40,7 @@ class TravelinfoTeaser extends AbstractEntity
/**
* @param string $title
*/
public function setTitle($title)
public function setTitle($title): void
{
$this->title = $title;
}
@@ -48,7 +50,7 @@ class TravelinfoTeaser extends AbstractEntity
return $this->bodytext;
}
public function setBodytext($bodytext)
public function setBodytext($bodytext): void
{
$this->bodytext = $bodytext;
}
@@ -58,7 +60,7 @@ class TravelinfoTeaser extends AbstractEntity
return $this->category;
}
public function setCategory($category)
public function setCategory($category): void
{
$this->category = $category;
}
@@ -68,9 +70,8 @@ class TravelinfoTeaser extends AbstractEntity
return $this->image;
}
public function setImage($image)
public function setImage($image): void
{
$this->image = $image;
}
}
@@ -1,33 +1,11 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/***************************************************************
*
* 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 Webcam extends AbstractEntity
{
@@ -67,7 +45,7 @@ class Webcam extends AbstractEntity
/**
* @param Snowreport $snowreport
*/
public function setSnowreport($snowreport)
public function setSnowreport($snowreport): void
{
$this->snowreport = $snowreport;
}
@@ -83,7 +61,7 @@ class Webcam extends AbstractEntity
/**
* @param int $vendorUid
*/
public function setVendorUid($vendorUid)
public function setVendorUid($vendorUid): void
{
$this->vendorUid = $vendorUid;
}
@@ -99,7 +77,7 @@ class Webcam extends AbstractEntity
/**
* @param string $name
*/
public function setName($name)
public function setName($name): void
{
$this->name = $name;
}
@@ -115,7 +93,7 @@ class Webcam extends AbstractEntity
/**
* @param string $url
*/
public function setUrl($url)
public function setUrl($url): void
{
$this->url = $url;
}
@@ -131,9 +109,8 @@ class Webcam extends AbstractEntity
/**
* @param boolean $available
*/
public function setAvailable($available)
public function setAvailable($available): void
{
$this->available = $available;
}
}
@@ -25,7 +25,6 @@ abstract class AbstractRepository extends Repository
}
/**
* @param array $uids
* @return QueryResultInterface
* @throws InvalidQueryException
*/
@@ -4,38 +4,15 @@ namespace EP\EpProducts\Domain\Repository;
use EP\EpProducts\Domain\Model\AbstractDestination;
/***************************************************************
*
* 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 CityRepository extends AbstractRepository
{
public function getTeasersByCountryOrRegionAndSeason(int $regionUid = 0, int $countryUid = 0, string $season = 'w', int $storagePid = 0)
{
if ($season === null) {
$season = AbstractDestination::SEASON_WINTER;
}
public function getTeasersByCountryOrRegionAndSeason(
int $regionUid = 0,
int $countryUid = 0,
string $season = 'w',
int $storagePid = 0,
) {
$season ??= AbstractDestination::SEASON_WINTER;
$query = $this->createQuery();
$constraints = [
$query->equals('season', $season),
@@ -1,31 +1,8 @@
<?php
declare(strict_types=1);
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!
***************************************************************/
class ConceptRepository extends AbstractRepository
{}
@@ -1,31 +1,8 @@
<?php
declare(strict_types=1);
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!
***************************************************************/
class CountryRepository extends AbstractRepository
{}
@@ -2,31 +2,6 @@
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;
@@ -2,30 +2,6 @@
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 TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
@@ -41,7 +17,7 @@ class HotelRepository extends AbstractRepository
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function getSingleByUid($hotelUid)
public function getSingleByUid($hotelUid): array|false
{
$qb = $this->getQueryBuilder();
$qb
@@ -59,7 +35,7 @@ class HotelRepository extends AbstractRepository
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function getDateIndependentSingleByUid($hotelUid)
public function getDateIndependentSingleByUid($hotelUid): array|false
{
$qb = $this->getDbConnection()->createQueryBuilder();
@@ -83,10 +59,9 @@ class HotelRepository extends AbstractRepository
/**
* @param int $productUid
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function getListByProduct($productUid)
public function getListByProduct($productUid): array
{
$qb = $this->getQueryBuilder(false);
$qb
@@ -98,11 +73,9 @@ class HotelRepository extends AbstractRepository
}
/**
* @param array $criteria
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function getListByCriteria(array $criteria)
public function getListByCriteria(array $criteria): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setDefined(['regionUid', 'countryUid', 'conceptUid', 'productUid', 'category', 'type']);
@@ -150,12 +123,9 @@ class HotelRepository extends AbstractRepository
}
/**
* @param array $criteria
* @param array $excludedHotelUids
* @return array
* @throws \Doctrine\DBAL\DBALException
*/
public function getDateIndependentListByCriteria(array $criteria, array $excludedHotelUids = [])
public function getDateIndependentListByCriteria(array $criteria, array $excludedHotelUids = []): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setDefined(['regionUid', 'countryUid', 'conceptUid', 'productUid', 'category', 'type']);
@@ -179,7 +149,7 @@ class HotelRepository extends AbstractRepository
$qb->getConcreteQueryBuilder()->orderBy('FIELD(hotel.type, ' . Hotel::TYPE_SPORTS_CLUB . ') DESC');
$qb->addOrderBy('hotel.name', 'ASC');
if ($excludedHotelUids) {
if ($excludedHotelUids !== []) {
$qb
->andWhere('hotel.uid NOT IN (:excludedHotelUids)')
->setParameter('excludedHotelUids', $excludedHotelUids, Connection::PARAM_INT_ARRAY)
@@ -225,7 +195,7 @@ class HotelRepository extends AbstractRepository
* @return QueryBuilder
* @throws \Doctrine\DBAL\DBALException
*/
protected function getQueryBuilder($excludeNonSearchable = true)
private function getQueryBuilder($excludeNonSearchable = true)
{
$qb = $this->getDbConnection()->createQueryBuilder();
@@ -244,7 +244,7 @@ class ProductRepository extends AbstractRepository
);
}
if ($date !== null) {
if ($date instanceof Date) {
$qb->andWhere($qb->expr()->eq(
'date.uid',
$qb->createNamedParameter($date->getUid(), Connection::PARAM_INT))
@@ -4,38 +4,11 @@ namespace EP\EpProducts\Domain\Repository;
use EP\EpProducts\Domain\Model\AbstractDestination;
/***************************************************************
*
* 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 RegionRepository extends AbstractRepository
{
public function getTeasersByCountryAndSeason(int $countryUid, string $season = 'w', int $storagePid = 0)
{
if ($season === null) {
$season = AbstractDestination::SEASON_WINTER;
}
$season ??= AbstractDestination::SEASON_WINTER;
$query = $this->createQuery();
$constraints = [
$query->equals('country', $countryUid),
@@ -31,7 +31,7 @@ class SnowreportRepository extends AbstractRepository
->orderBy('region.name')
;
if ($regionUids) {
if ($regionUids !== []) {
$qb->andWhere($qb->expr()->in(
'region.uid',
$qb->createNamedParameter($regionUids, Connection::PARAM_INT_ARRAY)
@@ -5,41 +5,17 @@ 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
*/
#[\Override]
protected $defaultOrderings = [
'sorting' => 'ASC',
];
/**
* @param array $categories
* @return array|QueryResultInterface
* @throws InvalidQueryException
*/
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Domain\Repository;
class TravelinfoRepository extends AbstractRepository
@@ -2,41 +2,12 @@
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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\Fact;
use EP\EpProducts\Utility\SerializationUtility;
class City
{
/**
* @param \EP\EpProducts\Domain\Model\City $entity
* @return array
*/
public static function fromEntity(\EP\EpProducts\Domain\Model\City $entity)
public static function fromEntity(\EP\EpProducts\Domain\Model\City $entity): array
{
$city = [];
@@ -46,7 +17,7 @@ class City
$city['party'] = SerializationUtility::sanitizeText($entity->getParty());
$city['leisure'] = SerializationUtility::sanitizeText($entity->getLeisure());
$city['facts'] = [];
$city['facts']['fact'] = array_map(function ($fact) {
$city['facts']['fact'] = array_map(function ($fact): string {
/** @var Fact $fact */
return htmlspecialchars($fact->getName());
}, $entity->getFacts()->toArray());
@@ -2,38 +2,9 @@
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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 Date
{
/**
* @param array $array
* @return array
*/
public static function fromArray(array $array)
public static function fromArray(array $array): array
{
$date = [
'servicesincluded' => [],
@@ -3,38 +3,9 @@ namespace EP\EpProducts\Domain\Serialization;
use EP\EpProducts\Utility\SerializationUtility;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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 Hotel
{
/**
* @param \EP\EpProducts\Domain\Model\Hotel $entity
* @return array
*/
public static function fromEntity(\EP\EpProducts\Domain\Model\Hotel $entity)
public static function fromEntity(\EP\EpProducts\Domain\Model\Hotel $entity): array
{
$hotel = [
'dates' => [],
@@ -2,31 +2,6 @@
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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\Fact;
use EP\EpProducts\Utility\SerializationUtility;
use TYPO3\CMS\Core\Site\SiteFinder;
@@ -35,17 +10,14 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
class Product
{
/**
* @param \EP\EpProducts\Domain\Model\Product $entity
* @param array $imageProcessingInstructions
* @param array $queryData
* @return array
*/
public static function fromEntity
(
\EP\EpProducts\Domain\Model\Product $entity,
?array $imageProcessingInstructions = null,
array $queryData = []
)
): array
{
$product = [
'concept' => [],
@@ -64,7 +36,7 @@ class Product
$product['name'] .= ' (' . $entity->getConcept()->getName() . ')';
}
$product['facts']['fact'] = array_map(function ($fact) {
$product['facts']['fact'] = array_map(function ($fact): string {
/** @var Fact $fact */
return htmlspecialchars($fact->getName());
}, $entity->getFacts()->toArray());
@@ -2,41 +2,12 @@
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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\Fact;
use EP\EpProducts\Utility\SerializationUtility;
class Region
{
/**
* @param \EP\EpProducts\Domain\Model\Region $entity
* @return array
*/
public static function fromEntity(\EP\EpProducts\Domain\Model\Region $entity)
public static function fromEntity(\EP\EpProducts\Domain\Model\Region $entity): array
{
$region = [];
@@ -50,7 +21,7 @@ class Region
$region['skipass'] = SerializationUtility::sanitizeText($entity->getSkiPass());
$region['news'] = SerializationUtility::sanitizeText($entity->getNews());
$region['facts'] = [];
$region['facts']['fact'] = array_map(function ($fact) {
$region['facts']['fact'] = array_map(function ($fact): string {
/** @var Fact $fact */
return htmlspecialchars($fact->getName());
}, $entity->getFacts()->toArray());
@@ -1,38 +1,9 @@
<?php
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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 Room
{
/**
* @param array $array
* @return array
*/
public static function fromArray(array $array)
public static function fromArray(array $array): array
{
$room = [];
@@ -1,37 +1,12 @@
<?php
namespace EP\EpProducts\Domain\Serialization;
/***************************************************************
*
* Copyright notice
*
* (c) 2018 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!
***************************************************************/
declare(strict_types=1);
namespace EP\EpProducts\Domain\Serialization;
class Services
{
/**
* @var array
*/
protected static $categoryLabels = [
private static array $categoryLabels = [
'BUS' => 'Anreise',
'VPF' => 'Verpflegung',
'SPA' => 'Skipass',
@@ -40,11 +15,7 @@ class Services
'SON' => 'Sonstiges',
];
/**
* @param array $array
* @return array
*/
public static function fromArray(array $array)
public static function fromArray(array $array): array
{
$services = [
'category' => [],
@@ -2,6 +2,7 @@
namespace EP\EpProducts\EpTeam;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\HttpClient\HttpClient;
@@ -25,15 +26,15 @@ class ApiClient implements LoggerAwareInterface
/**
* Applied when extension configuration does not override them. Seconds.
*/
private const DEFAULT_TIMEOUT = 5;
private const DEFAULT_MAX_DURATION = 15;
private const int DEFAULT_TIMEOUT = 5;
private const int DEFAULT_MAX_DURATION = 15;
/**
* Status codes the endpoint answers a stored submission with. A conflict is included for the
* dedupe the team application may add later - it is not returned today - because a
* double-clicked submit button is not something to report.
*/
private const ACCEPTED_STATUS_CODES = [
private const array ACCEPTED_STATUS_CODES = [
Response::HTTP_OK,
Response::HTTP_CREATED,
Response::HTTP_ACCEPTED,
@@ -46,7 +47,7 @@ class ApiClient implements LoggerAwareInterface
* offending field in its body, which is the only way to tell from a log why a submission
* was refused.
*/
private const ERROR_BODY_EXCERPT_LENGTH = 500;
private const int ERROR_BODY_EXCERPT_LENGTH = 500;
/**
* Shared, like the MyE&P client: Symfony's CurlHttpClient keeps its connection pool and TLS
@@ -126,7 +127,7 @@ class ApiClient implements LoggerAwareInterface
{
try {
$body = trim($response->getContent(false));
} catch (TransportExceptionInterface $e) {
} catch (TransportExceptionInterface) {
return '<no response body>';
}
@@ -135,7 +136,7 @@ class ApiClient implements LoggerAwareInterface
}
if (mb_strlen($body) > self::ERROR_BODY_EXCERPT_LENGTH) {
$body = mb_substr($body, 0, self::ERROR_BODY_EXCERPT_LENGTH) . '…';
return mb_substr($body, 0, self::ERROR_BODY_EXCERPT_LENGTH) . '…';
}
return $body;
@@ -151,13 +152,13 @@ class ApiClient implements LoggerAwareInterface
{
try {
$data = $response->toArray(false);
} catch (\Throwable $e) {
} catch (\Throwable) {
// A body that is absent (204) or not JSON costs nothing here - the submission is
// stored either way, which the asserted status has already established.
return;
}
if (null !== $this->logger) {
if ($this->logger instanceof LoggerInterface) {
$this->logger->info('Application form submission accepted by the EP Team API.', [
'form' => $payload['form'] ?? null,
'pageUid' => $payload['pageUid'] ?? null,
@@ -171,8 +172,8 @@ class ApiClient implements LoggerAwareInterface
*/
private function getHttpClient(): HttpClientInterface
{
if (null !== static::$httpClient) {
return static::$httpClient;
if (self::$httpClient instanceof HttpClientInterface) {
return self::$httpClient;
}
$config = $this->getConfiguration();
@@ -185,7 +186,7 @@ class ApiClient implements LoggerAwareInterface
if ('' === $apiKey) {
throw new ApiException('EP Team API key is not configured.');
}
if (0 !== strpos($baseUrl, 'https://')) {
if (!str_starts_with($baseUrl, 'https://')) {
// The application answers an http request with a 301 to the https URL, and this
// client does not follow redirects, so a plain http base URL fails every submission.
$this->logWarning(sprintf('EP Team API base URL is not https (%s); submissions will fail on the redirect.', $baseUrl));
@@ -215,29 +216,27 @@ class ApiClient implements LoggerAwareInterface
$options['max_duration'] = $maxDuration;
}
return static::$httpClient = HttpClient::createForBaseUri(rtrim($baseUrl, '/') . '/', $options);
return self::$httpClient = HttpClient::createForBaseUri(rtrim($baseUrl, '/') . '/', $options);
}
private function getConfiguration(): array
{
if (null === $this->configuration) {
$this->configuration = GeneralUtility::makeInstance(ExtensionConfiguration::class)
->get('ep_products');
}
$this->configuration ??= GeneralUtility::makeInstance(ExtensionConfiguration::class)
->get('ep_products');
return $this->configuration;
}
private function logError(string $message, array $context = []): void
{
if (null !== $this->logger) {
if ($this->logger instanceof LoggerInterface) {
$this->logger->error($message, $context);
}
}
private function logWarning(string $message, array $context = []): void
{
if (null !== $this->logger) {
if ($this->logger instanceof LoggerInterface) {
$this->logger->warning($message, $context);
}
}
Loaded 100 of 259 files, more files were not shown because too many files have changed in this diff. Show more