Markets List and Detail Plugin
This commit is contained in:
@@ -34,6 +34,8 @@ this extension turns every content element into clean **JSON** for a React front
|
||||
- [Content elements & plugins](#content-elements--plugins)
|
||||
- [Content Blocks](#content-blocks)
|
||||
- [Layout containers](#layout-containers)
|
||||
- [Forms](#forms)
|
||||
- [Page‑level fields](#pagelevel-fields)
|
||||
- [Structured data (JSON‑LD)](#structured-data-json-ld)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
@@ -52,6 +54,8 @@ this extension turns every content element into clean **JSON** for a React front
|
||||
children inside layout containers.
|
||||
- 🎛️ **Editor‑friendly Content Blocks** — hero, cards, CTA, FAQ, video, intro and a
|
||||
two‑column layout block, all with a unified header section.
|
||||
- 📮 **Forms without a form framework** — one PHP definition drives both the JSON the
|
||||
React app renders *and* the server‑side validation of the submission.
|
||||
- 🔎 **SEO built in** — a schema.org `@graph` (Organization, Product, FAQ, Events,
|
||||
News …) is emitted per page.
|
||||
- 🛡️ **Fail‑soft** — a failing element yields empty output, never a broken page.
|
||||
@@ -78,6 +82,9 @@ HTTP request (headless: 1)
|
||||
│
|
||||
▼
|
||||
[L6] Structured data PageJsonLdRenderer + StructuredDataService → @graph
|
||||
│
|
||||
▼
|
||||
[L7] Page-level fields MenuProcessor + FaviconsJsonRenderer → menus, favicons
|
||||
```
|
||||
|
||||
Each renderer follows one pattern — an `#[AsAllowedCallable] render()` for top‑level use
|
||||
@@ -94,12 +101,23 @@ as the reference).
|
||||
|---|---|---|---|
|
||||
| Products | `vitec_productlist` / `vitec_productshow` | `ProductList/ProductShowJsonRenderer` | `products` / `product` |
|
||||
| Success Stories | `vitec_usecaselist` / `vitec_usecaseshow` | `UsecaseList/ShowJsonRenderer` → `UsecaseSerializer` | `usecases` / `usecase` |
|
||||
| Markets | `vitec_marketshow` | `MarketShowJsonRenderer` | `market` |
|
||||
| Markets | `vitec_marketlist` / `vitec_marketshow` | `MarketList/MarketShowJsonRenderer` | `markets` / `market` |
|
||||
| Solutions | `vitec_solutionshow` | `SolutionShowJsonRenderer` | `solution` |
|
||||
| Downloads | `vitec_downloadcard` / `vitec_downloadcardcollection` | `Downloadcard*JsonRenderer` | `downloadcard` / `downloadcardcollection` |
|
||||
| Datasheets | `vitec_datasheets` | `DatasheetsJsonRenderer` | `datasheets` |
|
||||
| Events | `vitec_eventlist` | `EventlistJsonRenderer` | `eventlist` |
|
||||
| News | `news_pi1` (+ variants) | `NewsJsonRenderer` | `news` |
|
||||
| Locations | `vitec_locationlist` | `LocationsJsonRenderer` | `locations` |
|
||||
| Customer logos | `vitec_customerlogos` | `CustomerlogosJsonRenderer` | `customerlogos` |
|
||||
| Cards | `vitec_modelcard` | `ModelcardJsonRenderer` | `card` |
|
||||
| Forms | `vitec_contactform` / `vitec_demoform` / `vitec_helpdeskform` | `FormsJsonRenderer` | `form` |
|
||||
| News | `news_pi1` (+ 8 variants) | `NewsJsonRenderer` | `news` |
|
||||
|
||||
**Cards** (`vitec_modelcard`) are one plugin for four model types — the FlexForm picks
|
||||
`product`, `story`, `market` or `solution` plus a record, and every card field comes
|
||||
from that record. Image resolution is delegated to `UsecaseSerializer::image()`.
|
||||
|
||||
> ⚠️ `vitec_simplecard` is registered as a plugin and offered in the wizard, but has
|
||||
> no JSON renderer — it emits no payload in headless mode. See Annex B‑8 of the spec.
|
||||
|
||||
## Content Blocks
|
||||
|
||||
@@ -130,6 +148,40 @@ Nested column grids (`b13/container`) that own child content elements and emit t
|
||||
| `vitec_container` | Single column with a custom CSS class |
|
||||
| `vitec_cards_carousel` | Carousel of card elements |
|
||||
|
||||
## Forms
|
||||
|
||||
Three form plugins (contact · demo · helpdesk) share one renderer and one definition.
|
||||
[`FormDefinitions`](Classes/Forms/FormDefinitions.php) is the single source of truth:
|
||||
the same field list produces the JSON the React app renders **and** validates the
|
||||
submission server‑side.
|
||||
|
||||
```
|
||||
GET page JSON → content.form = { formKey, title, endpoint, honeypot, fields[] }
|
||||
POST /api/vitec/form/<formKey> → { "success": true } | 422 { success:false, errors{} }
|
||||
```
|
||||
|
||||
`FormSubmissionMiddleware` handles the endpoint: honeypot → validation → store in
|
||||
`tx_vitec_form_submission` → deliver. Delivery is a strategy
|
||||
([`DeliveryInterface`](Classes/Forms/Delivery/DeliveryInterface.php)) with
|
||||
`EmailDelivery` (active) and `SalesforceDelivery` (**prepared stub — `deliver()` always
|
||||
throws**), chosen per form via the FlexForm. Because the submission is stored *before*
|
||||
delivery is attempted, a failed delivery never loses data — it is recorded as
|
||||
`delivery_status = failed` on the record and the endpoint still answers `success: true`.
|
||||
|
||||
## Page‑level fields
|
||||
|
||||
Beyond `content[]`, every page response carries:
|
||||
|
||||
| Field | Source |
|
||||
|---|---|
|
||||
| `mainNavigation` / `footerMenu` / `metaMenu` | headless `MenuProcessor`; the curated menus are driven by the site settings `menu.footer.pageUids` / `menu.meta.pageUids` |
|
||||
| `favicons` | `FaviconsJsonRenderer` — ready‑to‑render `<link>` descriptors plus `themeColor` |
|
||||
| `jsonLd` | `PageJsonLdRenderer` (see below) |
|
||||
|
||||
Two frontend middlewares run before page resolution: `vitec/form-submission` (the form
|
||||
endpoint) and `vitec/success-story-path-rewrite`, which lets the public SEO URL
|
||||
`/success-stories/<slug>` resolve to the detail subpage without changing the browser URL.
|
||||
|
||||
## Structured data (JSON‑LD)
|
||||
|
||||
`PageJsonLdRenderer` + `StructuredDataService` assemble a schema.org `@graph` per page:
|
||||
@@ -177,15 +229,18 @@ The short version (full normative rules in the architecture spec, Clause 9 &
|
||||
2. **Serializer** — `Classes/Service/<Domain>Serializer` with `serializeListItem()` / `serializeDetail()`.
|
||||
3. **Renderer** — `Classes/UserFunc/<Domain><Kind>JsonRenderer` with
|
||||
`#[AsAllowedCallable] render()` + `renderForRecord()`, delegating to the serializer.
|
||||
4. **TypoScript** — in `Configuration/Sets/Vitecset/setup.typoscript`:
|
||||
4. **Registration** — `configurePlugin()` in `ext_localconf.php`, a FlexForm, an icon
|
||||
and a wizard entry in `Configuration/page.tsconfig`.
|
||||
5. **TypoScript** — in `Configuration/Sets/Vitecset/setup.typoscript`:
|
||||
```typoscript
|
||||
tt_content.<ctype> < lib.contentElementWithHeader
|
||||
tt_content.<ctype>.fields.content.fields.<key> = USER
|
||||
tt_content.<ctype>.fields.content.fields.<key>.userFunc = Evomedien\Vitec\UserFunc\<Class>->render
|
||||
```
|
||||
5. **Nesting** — if it may sit inside a container, register it in `PLUGIN_RENDERERS`
|
||||
Skipping this step is exactly what leaves a plugin payload‑less (see `vitec_simplecard`).
|
||||
6. **Nesting** — if it may sit inside a container, register it in `PLUGIN_RENDERERS`
|
||||
(in both `ContentElementResolver` and `ContainerChildrenProcessor`).
|
||||
6. **Deploy** — `database:updateschema "*.add,*.change"` & `cache:flush`.
|
||||
7. **Deploy** — `database:updateschema "*.add,*.change"` & `cache:flush`.
|
||||
|
||||
> **Conventions:** list keys are plural, detail keys singular; page‑id via the
|
||||
> `frontend.page.information` request attribute; container CTypes derive with `<` (copy),
|
||||
@@ -202,9 +257,11 @@ packages/vitec/
|
||||
│ ├── UserFunc/ # JSON renderers (one per plugin) — headless entry points
|
||||
│ ├── Service/ # Serializers, ContentElementResolver, StructuredDataService
|
||||
│ ├── DataProcessing/ # ContainerChildrenProcessor (container → items)
|
||||
│ ├── Forms/ # FormDefinitions + Delivery/ (email, salesforce)
|
||||
│ ├── Middleware/ # form endpoint, success-story path rewrite
|
||||
│ ├── Domain/Model|Repository/
|
||||
│ ├── Controller/ # Extbase controllers (non-headless / backend)
|
||||
│ └── Backend/ · View/ · Hook/ · EventListener/
|
||||
│ └── Backend/ · View/ · Hook/ · EventListener/ · Tca/ · Preview/
|
||||
├── ContentBlocks/
|
||||
│ └── ContentElements/ # card, cta-banner, columns, faq, hero-section, intro-paragraph, video
|
||||
├── Configuration/
|
||||
|
||||
Reference in New Issue
Block a user