Initial Commit
This commit is contained in:
107
Documentation/CustomFrameClasses.md
Normal file
107
Documentation/CustomFrameClasses.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# Custom Frame Classes for TYPO3 Content Elements
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Custom frame class options have been added to the TYPO3 backend, allowing editors to select Vitec-specific styling options for content elements.
|
||||||
|
|
||||||
|
## Location
|
||||||
|
|
||||||
|
File: `/packages/vitec/Configuration/TCA/Overrides/tt_content.php`
|
||||||
|
|
||||||
|
## Available Frame Classes
|
||||||
|
|
||||||
|
The following custom frame classes are available in the backend under **Appearance → Frame**:
|
||||||
|
|
||||||
|
| Label | CSS Class | Description |
|
||||||
|
|-------|-----------|-------------|
|
||||||
|
| Vitec: Full Width | `vitec-full-width` | Content spans full width of the viewport |
|
||||||
|
| Vitec: Centered Container | `vitec-centered` | Content is centered with constrained width |
|
||||||
|
| Vitec: Card Style | `vitec-card` | Content displayed as a card with shadow/border |
|
||||||
|
| Vitec: Dark Background | `vitec-dark` | Content with dark background styling |
|
||||||
|
| Vitec: Highlight Box | `vitec-highlight` | Content with highlighted/accent styling |
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
The frame classes are added using TCA overrides, with the field configured to allow **multiple selections**:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Change frame_class to allow multiple selections
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['renderType'] = 'selectCheckBox';
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['maxitems'] = 999;
|
||||||
|
|
||||||
|
// Add custom frame_class options for Vitec
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'] = array_merge(
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'],
|
||||||
|
[
|
||||||
|
['label' => 'Vitec: Full Width', 'value' => 'vitec-full-width'],
|
||||||
|
['label' => 'Vitec: Centered Container', 'value' => 'vitec-centered'],
|
||||||
|
['label' => 'Vitec: Card Style', 'value' => 'vitec-card'],
|
||||||
|
['label' => 'Vitec: Dark Background', 'value' => 'vitec-dark'],
|
||||||
|
['label' => 'Vitec: Highlight Box', 'value' => 'vitec-highlight'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### In Backend
|
||||||
|
|
||||||
|
1. Edit any content element
|
||||||
|
2. Navigate to the **Appearance** tab
|
||||||
|
3. **Select one or more options** using checkboxes from the **Frame** field
|
||||||
|
4. Multiple classes can be combined (e.g., "Card Style" + "Dark Background")
|
||||||
|
5. Save the content element
|
||||||
|
|
||||||
|
### In Frontend/CSS
|
||||||
|
|
||||||
|
The selected frame class is added to the content element wrapper. Style these classes in your CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Example CSS styling */
|
||||||
|
.vitec-full-width {
|
||||||
|
width: 100vw;
|
||||||
|
margin-left: calc(-50vw + 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vitec-centered {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vitec-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vitec-dark {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vitec-highlight {
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
border-left: 4px solid #0066cc;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding More Options
|
||||||
|
|
||||||
|
To add additional frame class options, edit `/packages/vitec/Configuration/TCA/Overrides/tt_content.php` and add new array entries:
|
||||||
|
|
||||||
|
```php
|
||||||
|
['label' => 'Your Label', 'value' => 'your-css-class'],
|
||||||
|
```
|
||||||
|
|
||||||
|
After making changes, clear the TYPO3 cache:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./vendor/bin/typo3 cache:flush
|
||||||
|
```
|
||||||
|
|
||||||
|
## Date Added
|
||||||
|
|
||||||
|
November 19, 2025
|
||||||
624
VITEC-ContentBlocks-Setup.md
Normal file
624
VITEC-ContentBlocks-Setup.md
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
# VITEC Content Blocks Setup — Step-by-Step
|
||||||
|
|
||||||
|
**Strategie:** Content Blocks (YAML-basiert, deklarativ) als Haupt-Pattern für alle VITEC Content Elements. `nb-headless-content-blocks` übernimmt automatisch das JSON-Mapping.
|
||||||
|
|
||||||
|
**Stack-Basis bei dir (bereits vorhanden):**
|
||||||
|
- `evomedien/vitec` — deine Extension
|
||||||
|
- Site Set `evomedien/vitecset`
|
||||||
|
- `friendsoftypo3/headless` (`headless: 1`)
|
||||||
|
- `nb-headless-content-blocks` (Bridge)
|
||||||
|
- `b13/container` + `itplusx/headless-container` (für Layout-Wrapper, optional)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Aufräumen (zwei kleine Issues vorab)
|
||||||
|
|
||||||
|
### 0.1 `nb-headless-content-blocks` ins Site Set migrieren
|
||||||
|
|
||||||
|
Aktuell steht die Extension nur in der **Site-Config**, nicht im **Site Set**. Das sollte konsistent sein, damit das Set in sich abgeschlossen ist.
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/Configuration/Sets/VitecSet/config.yaml` (oder wo dein Set liegt)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: evomedien/vitecset
|
||||||
|
label: VITEC Set
|
||||||
|
settings:
|
||||||
|
website:
|
||||||
|
background:
|
||||||
|
color: '#386492'
|
||||||
|
dependencies:
|
||||||
|
- typo3/fluid-styled-content
|
||||||
|
- friendsoftypo3/headless
|
||||||
|
- b13/container
|
||||||
|
- itplusx/headless-container
|
||||||
|
- nb-headless-content-blocks/headless-content-blocks # NEU
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann aus `config/sites/<identifier>/config.yaml` die Zeile `- nb-headless-content-blocks/headless-content-blocks` wieder rausnehmen — sie kommt jetzt transitiv über dein Set.
|
||||||
|
|
||||||
|
### 0.2 Version-Mismatch fixen
|
||||||
|
|
||||||
|
- `composer.json` sagt `1.0.1`
|
||||||
|
- `ext_emconf.php` sagt `0.0.1`
|
||||||
|
|
||||||
|
Einfach beide auf den gleichen Wert bringen (Empfehlung: `1.0.0` und bei jedem Deployment hochzählen). Nicht dringend, aber sauber:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// ext_emconf.php
|
||||||
|
'version' => '1.0.0',
|
||||||
|
'state' => 'stable', // 'alpha' wirkt im Produktivbetrieb komisch
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
// composer.json
|
||||||
|
"version": "1.0.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Ordnerstruktur anlegen
|
||||||
|
|
||||||
|
Content Blocks leben in der Extension unter einer festen Pfad-Konvention:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/vitec/
|
||||||
|
├── ContentBlocks/
|
||||||
|
│ └── ContentElements/
|
||||||
|
│ ├── hero-section/ ← erster Content Block (Beispiel)
|
||||||
|
│ │ ├── config.yaml ← Feld-Definitionen
|
||||||
|
│ │ ├── assets/
|
||||||
|
│ │ │ └── icon.svg ← Icon für CE-Wizard
|
||||||
|
│ │ ├── language/
|
||||||
|
│ │ │ └── labels.xlf ← Übersetzungen
|
||||||
|
│ │ └── templates/
|
||||||
|
│ │ └── EditorPreview.html ← optional: Backend-Preview
|
||||||
|
│ ├── feature-teaser/
|
||||||
|
│ ├── cta-banner/
|
||||||
|
│ └── ...
|
||||||
|
├── Classes/
|
||||||
|
├── Configuration/
|
||||||
|
├── composer.json
|
||||||
|
└── ext_emconf.php
|
||||||
|
```
|
||||||
|
|
||||||
|
Ein Content Block = ein Ordner. Name = Ordnername (kleingeschrieben, mit Bindestrichen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Erster Content Block: VITEC Hero Section
|
||||||
|
|
||||||
|
Nachgebaut nach einem typischen Template aus deinen Figma-Wireframes: Eyebrow + Heading + Subline + Body + CTA + Hero-Image + Background-Variant.
|
||||||
|
|
||||||
|
### 2.1 `config.yaml`
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/ContentBlocks/ContentElements/hero-section/config.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: vitec/hero-section
|
||||||
|
group: vitec
|
||||||
|
prefixFields: true
|
||||||
|
prefixType: full
|
||||||
|
|
||||||
|
fields:
|
||||||
|
- identifier: eyebrow
|
||||||
|
type: Text
|
||||||
|
max: 50
|
||||||
|
|
||||||
|
- identifier: header
|
||||||
|
useExistingField: true
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- identifier: subheader
|
||||||
|
useExistingField: true
|
||||||
|
|
||||||
|
- identifier: bodytext
|
||||||
|
useExistingField: true
|
||||||
|
enableRichtext: true
|
||||||
|
|
||||||
|
- identifier: cta
|
||||||
|
type: Link
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: cta_label
|
||||||
|
type: Text
|
||||||
|
default: 'Learn more'
|
||||||
|
max: 30
|
||||||
|
|
||||||
|
- identifier: hero_image
|
||||||
|
type: File
|
||||||
|
minitems: 0
|
||||||
|
maxitems: 1
|
||||||
|
allowed: common-image-types
|
||||||
|
extendedPalette: true
|
||||||
|
|
||||||
|
- identifier: background_variant
|
||||||
|
type: Select
|
||||||
|
renderType: selectSingle
|
||||||
|
default: none
|
||||||
|
items:
|
||||||
|
- label: None
|
||||||
|
value: none
|
||||||
|
- label: Orange
|
||||||
|
value: orange
|
||||||
|
- label: Blue
|
||||||
|
value: blue
|
||||||
|
- label: Graphite
|
||||||
|
value: graphite
|
||||||
|
- label: Midnight
|
||||||
|
value: midnight
|
||||||
|
|
||||||
|
- identifier: show_logo_wall
|
||||||
|
type: Checkbox
|
||||||
|
default: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Was passiert hier:**
|
||||||
|
|
||||||
|
- `name: vitec/hero-section` → TCA-Typ `vitec_hero_section`, Tabelle `tt_content`
|
||||||
|
- `group: vitec` → eigene Sektion im CE-Wizard (muss noch registriert werden — siehe 2.5)
|
||||||
|
- `useExistingField: true` → recycled TYPO3-Core-Felder (`header`, `subheader`, `bodytext`). Keine neuen DB-Spalten, konsistente Bedeutung
|
||||||
|
- `prefixFields: true` + `prefixType: full` → neue Felder bekommen `tx_vitec_hero_section_*` als DB-Prefix (vermeidet Kollisionen)
|
||||||
|
- **Keine TCA-Override-Datei nötig** — Content Blocks generiert das TCA automatisch
|
||||||
|
|
||||||
|
### 2.2 `language/labels.xlf`
|
||||||
|
|
||||||
|
Content Blocks erwartet XLF-Files nach einer festen Namens-Konvention. Jedes Feld bekommt automatisch einen Key `<vendor>.<element>.<field>.label`.
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/ContentBlocks/ContentElements/hero-section/language/labels.xlf`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xliff version="1.2">
|
||||||
|
<file source-language="en" datatype="plaintext" original="messages">
|
||||||
|
<body>
|
||||||
|
<!-- Element-Labels -->
|
||||||
|
<trans-unit id="title">
|
||||||
|
<source>VITEC · Hero Section</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="description">
|
||||||
|
<source>Haupt-Hero mit Headline, CTA, Background-Variante</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Field-Labels -->
|
||||||
|
<trans-unit id="eyebrow.label">
|
||||||
|
<source>Eyebrow Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="eyebrow.description">
|
||||||
|
<source>Kleiner Label-Text über der Headline</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="cta.label">
|
||||||
|
<source>CTA Link</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="cta_label.label">
|
||||||
|
<source>CTA Button Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="hero_image.label">
|
||||||
|
<source>Hero Image</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="background_variant.label">
|
||||||
|
<source>Background Variant</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="show_logo_wall.label">
|
||||||
|
<source>Show Logo Wall below</source>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
|
```
|
||||||
|
|
||||||
|
> Die Core-Felder (`header`, `subheader`, `bodytext`) brauchen keine Labels — sie erben die vom TYPO3-Core.
|
||||||
|
|
||||||
|
### 2.3 `assets/icon.svg`
|
||||||
|
|
||||||
|
Content Blocks erkennt Icons automatisch, wenn sie unter `assets/icon.svg` (oder `icon.png`) liegen.
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/ContentBlocks/ContentElements/hero-section/assets/icon.svg`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#F47937" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||||||
|
<path d="M3 9h18"/>
|
||||||
|
<circle cx="8" cy="15" r="2"/>
|
||||||
|
<path d="M13 15h5M13 18h3"/>
|
||||||
|
</svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
Platzhalter — tausch später gegen die echten VITEC-Icons aus.
|
||||||
|
|
||||||
|
### 2.4 `templates/EditorPreview.html` (optional aber empfohlen)
|
||||||
|
|
||||||
|
Im **Headless-Mode** wird kein Frontend-Template gerendert. Aber für die Redakteure ist eine Backend-Preview sinnvoll, damit sie im Seiten-Modul sehen, was sie gerade anlegen.
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/ContentBlocks/ContentElements/hero-section/templates/EditorPreview.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div style="padding: 1rem; border-left: 4px solid #F47937; background: #f8f8f8;">
|
||||||
|
<div style="font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: #666;">
|
||||||
|
VITEC · Hero Section — {data.background_variant}
|
||||||
|
</div>
|
||||||
|
<f:if condition="{data.eyebrow}">
|
||||||
|
<div style="font-size: 0.75rem; color: #F47937; margin-top: 0.5rem;">{data.eyebrow}</div>
|
||||||
|
</f:if>
|
||||||
|
<h3 style="margin: 0.25rem 0;">{data.header}</h3>
|
||||||
|
<f:if condition="{data.subheader}">
|
||||||
|
<div style="color: #555;">{data.subheader}</div>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{data.cta_label}">
|
||||||
|
<div style="margin-top: 0.5rem;">
|
||||||
|
<span style="display: inline-block; padding: 0.25rem 0.75rem; background: #F47937; color: white; border-radius: 3px; font-size: 0.8rem;">
|
||||||
|
{data.cta_label} →
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 Backend-Gruppe "VITEC" registrieren
|
||||||
|
|
||||||
|
Damit `group: vitec` aus der YAML eine saubere Sektion im CE-Wizard wird, muss die Gruppe einmal registriert sein:
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/Configuration/page.tsconfig` (deine existierende Datei — aktuell leer)
|
||||||
|
|
||||||
|
```tsconfig
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec {
|
||||||
|
header = VITEC
|
||||||
|
show = *
|
||||||
|
elements {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Das reicht — Content Blocks hängt die einzelnen Elemente dann automatisch unter `elements` ein.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Installation & Test
|
||||||
|
|
||||||
|
### 3.1 Caches und TCA flushen
|
||||||
|
|
||||||
|
Nach jeder Content-Block-Änderung:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vendor/bin/typo3 cache:flush
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder im Backend: Admin Tools → Maintenance → Flush all caches.
|
||||||
|
|
||||||
|
### 3.2 Datenbank-Schema updaten
|
||||||
|
|
||||||
|
Content Blocks erstellt automatisch neue Spalten (z.B. `tx_vitec_hero_section_eyebrow`, `tx_vitec_hero_section_background_variant` etc.):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vendor/bin/typo3 database:updateschema
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder Backend: Admin Tools → Maintenance → Analyze Database Structure → Run.
|
||||||
|
|
||||||
|
### 3.3 Im Backend anlegen
|
||||||
|
|
||||||
|
1. Seite im Seitenbaum öffnen → Page-Modul
|
||||||
|
2. "Neues Inhaltselement" → Tab **VITEC** → "VITEC · Hero Section"
|
||||||
|
3. Felder ausfüllen (Eyebrow, Heading, CTA, Image, Background-Variant "orange")
|
||||||
|
4. Speichern
|
||||||
|
|
||||||
|
### 3.4 JSON prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s https://dev.vitec.com/testseite | jq '.content.colPos0[] | select(.type == "vitec_hero_section")'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erwarteter JSON-Output** (durch `nb-headless-content-blocks` automatisch erzeugt):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"type": "vitec_hero_section",
|
||||||
|
"colPos": 0,
|
||||||
|
"categories": "",
|
||||||
|
"appearance": {
|
||||||
|
"layout": "default",
|
||||||
|
"frameClass": "default",
|
||||||
|
"spaceBefore": "",
|
||||||
|
"spaceAfter": ""
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"header": "Enterprise Video Solutions",
|
||||||
|
"subheader": "Reliable. Scalable. Proven.",
|
||||||
|
"bodytext": "<p>VITEC delivers mission-critical video...</p>",
|
||||||
|
"tx_vitec_hero_section_eyebrow": "Why VITEC",
|
||||||
|
"tx_vitec_hero_section_cta": {
|
||||||
|
"href": "/success-stories",
|
||||||
|
"target": null,
|
||||||
|
"class": null,
|
||||||
|
"title": null,
|
||||||
|
"linkText": "t3://page?uid=15",
|
||||||
|
"additionalAttributes": []
|
||||||
|
},
|
||||||
|
"tx_vitec_hero_section_cta_label": "Learn more",
|
||||||
|
"tx_vitec_hero_section_hero_image": [
|
||||||
|
{
|
||||||
|
"publicUrl": "https://dev.vitec.com/fileadmin/.../hero.png",
|
||||||
|
"properties": { ... }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tx_vitec_hero_section_background_variant": "orange",
|
||||||
|
"tx_vitec_hero_section_show_logo_wall": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Struktur passt zu deinem existierenden `textpic`-Pattern — `content.*` mit allen Feldern nebeneinander. ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. JSON-Schema aufräumen (Field-Namen kürzen)
|
||||||
|
|
||||||
|
Du hast sicher gemerkt: die Feldnamen im JSON sind lang (`tx_vitec_hero_section_eyebrow`). Das kommt vom `prefixFields: true` — nötig für DB-Konsistenz, aber unschön fürs Frontend.
|
||||||
|
|
||||||
|
**Lösung:** Via `nb-headless-content-blocks` EventListener die Keys umbenennen.
|
||||||
|
|
||||||
|
### 4.1 EventListener anlegen
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/Classes/EventListener/NormalizeContentBlockKeys.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\EventListener;
|
||||||
|
|
||||||
|
use Netzbewegung\NbHeadlessContentBlocks\Event\ModifyArrayRecursiveToArrayEvent;
|
||||||
|
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||||
|
|
||||||
|
#[AsEventListener(identifier: 'vitec/normalize-content-block-keys')]
|
||||||
|
final class NormalizeContentBlockKeys
|
||||||
|
{
|
||||||
|
public function __invoke(ModifyArrayRecursiveToArrayEvent $event): void
|
||||||
|
{
|
||||||
|
$key = $event->getKey();
|
||||||
|
|
||||||
|
// Strip prefix "tx_vitec_<element>_" from field names
|
||||||
|
// e.g. "tx_vitec_hero_section_eyebrow" → "eyebrow"
|
||||||
|
if (preg_match('/^tx_vitec_[a-z_]+?_([a-z_]+)$/', $key, $matches)) {
|
||||||
|
$event->setKey($matches[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Services.yaml
|
||||||
|
|
||||||
|
**Datei:** `packages/vitec/Configuration/Services.yaml` (anlegen falls nicht vorhanden)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
public: false
|
||||||
|
|
||||||
|
Evomedien\Vitec\:
|
||||||
|
resource: '../Classes/*'
|
||||||
|
exclude: '../Classes/Domain/Model/*'
|
||||||
|
```
|
||||||
|
|
||||||
|
> `AsEventListener` Attribute + autoconfigure = Event-Listener wird automatisch registriert. Keine weitere Konfiguration nötig.
|
||||||
|
|
||||||
|
### 4.3 Resultat nach Cache-Flush
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "vitec_hero_section",
|
||||||
|
"content": {
|
||||||
|
"header": "Enterprise Video Solutions",
|
||||||
|
"eyebrow": "Why VITEC",
|
||||||
|
"cta": { ... },
|
||||||
|
"cta_label": "Learn more",
|
||||||
|
"hero_image": [ ... ],
|
||||||
|
"background_variant": "orange",
|
||||||
|
"show_logo_wall": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Saubere, lesbare Keys — React-Kollege glücklich.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Pattern für weitere Content Blocks
|
||||||
|
|
||||||
|
Für jeden neuen Content Block brauchst du:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/vitec/ContentBlocks/ContentElements/<name>/
|
||||||
|
├── config.yaml ← Felder definieren
|
||||||
|
├── language/labels.xlf ← Labels übersetzen
|
||||||
|
├── assets/icon.svg ← Icon
|
||||||
|
└── templates/EditorPreview.html ← optional
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann:
|
||||||
|
```bash
|
||||||
|
vendor/bin/typo3 database:updateschema
|
||||||
|
vendor/bin/typo3 cache:flush
|
||||||
|
```
|
||||||
|
|
||||||
|
Fertig. Keine PHP-Boilerplate, keine TypoScript-Overrides, kein TCA-Gefummel.
|
||||||
|
|
||||||
|
### Template-Vorschlag für deine 20 Figma-Templates
|
||||||
|
|
||||||
|
Nummerierung/Naming-Vorschlag an den Figma-Templates entlang:
|
||||||
|
|
||||||
|
| # | Name | Content Block |
|
||||||
|
|---|---|---|
|
||||||
|
| 01 | Hero | `vitec/hero-section` |
|
||||||
|
| 02 | Feature-Teaser Grid | `vitec/feature-teaser-grid` |
|
||||||
|
| 03 | CTA Banner | `vitec/cta-banner` |
|
||||||
|
| 04 | Quote / Testimonial | `vitec/testimonial` |
|
||||||
|
| 05 | Logo Wall | `vitec/logo-wall` |
|
||||||
|
| 06 | Product Card Grid | `vitec/product-card-grid` |
|
||||||
|
| ... | ... | ... |
|
||||||
|
| 16 | Content Page V1 | `vitec/content-page-v1` |
|
||||||
|
|
||||||
|
Wenn du einen Block gebaut hast, ist jeder weitere 10-20 Minuten Arbeit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Wann brauchst du trotzdem `b13/container`?
|
||||||
|
|
||||||
|
**Antwort:** Für echte Layout-Wrapper mit nested Content Elements. Beispiele:
|
||||||
|
|
||||||
|
- **2-Spalten-Section:** Ein Container, in dessen linker Spalte ein `hero-section` + rechts ein `testimonial` liegt
|
||||||
|
- **Tabs / Accordion:** ein Tab-Container mit mehreren Content Blocks je Tab
|
||||||
|
- **Grid mit freier CE-Wahl:** 3-column-grid, wo der Redakteur pro Spalte frei wählt
|
||||||
|
|
||||||
|
Für diese Fälle:
|
||||||
|
|
||||||
|
1. `b13/container` Container registrieren (wie in der vorherigen Anleitung beschrieben, per PHP TCA-Override)
|
||||||
|
2. `itplusx/headless-container` mappt ihn automatisch ins JSON
|
||||||
|
3. Die Kinder sind dann Content Blocks → das Pattern spielt sauber zusammen
|
||||||
|
|
||||||
|
Laut Doku von `nb-headless-content-blocks`: **"Support for EXT:container"** ist eingebaut — die zwei Extensions beißen sich nicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Advanced: Sammlungen (Collections)
|
||||||
|
|
||||||
|
Für wiederkehrende Items (z.B. 3 Teaser-Cards in einem Grid) gibt es den `Collection`-Type:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: vitec/feature-teaser-grid
|
||||||
|
group: vitec
|
||||||
|
fields:
|
||||||
|
- identifier: header
|
||||||
|
useExistingField: true
|
||||||
|
|
||||||
|
- identifier: teasers
|
||||||
|
type: Collection
|
||||||
|
minitems: 1
|
||||||
|
maxitems: 6
|
||||||
|
fields:
|
||||||
|
- identifier: icon
|
||||||
|
type: File
|
||||||
|
maxitems: 1
|
||||||
|
allowed: common-image-types
|
||||||
|
- identifier: title
|
||||||
|
type: Text
|
||||||
|
required: true
|
||||||
|
- identifier: description
|
||||||
|
type: Textarea
|
||||||
|
- identifier: link
|
||||||
|
type: Link
|
||||||
|
```
|
||||||
|
|
||||||
|
Im JSON kommt das dann als Array raus:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "vitec_feature_teaser_grid",
|
||||||
|
"content": {
|
||||||
|
"header": "Our Solutions",
|
||||||
|
"teasers": [
|
||||||
|
{ "title": "...", "description": "...", "icon": [...], "link": {...} },
|
||||||
|
{ "title": "...", "description": "...", "icon": [...], "link": {...} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Cheatsheet: Field-Types
|
||||||
|
|
||||||
|
| YAML `type` | Zweck | JSON-Output-Typ |
|
||||||
|
|---|---|---|
|
||||||
|
| `Text` | Einzeiliger Text | string |
|
||||||
|
| `Textarea` | Mehrzeilig; mit `enableRichtext: true` → RTE | string (HTML bei RTE) |
|
||||||
|
| `Number` | int/float | number |
|
||||||
|
| `Checkbox` | Boolean | boolean |
|
||||||
|
| `Select` `renderType: selectSingle` | Dropdown | string (value) |
|
||||||
|
| `Select` `renderType: selectMultipleSideBySide` | Multi-Select | string (comma-sep) |
|
||||||
|
| `Radio` | Radio-Button-Gruppe | string |
|
||||||
|
| `Link` | TYPO3-Link (Page/URL/File/Email) | object (`href`, `target`, `linkText`…) |
|
||||||
|
| `File` | File-Reference | array of file-objects |
|
||||||
|
| `Color` | Color-Picker | string (hex) |
|
||||||
|
| `DateTime` | Datum/Zeit | string (ISO) |
|
||||||
|
| `Collection` | Wiederholbare Feldgruppen | array of objects |
|
||||||
|
| `Category` | TYPO3-Kategorien | array |
|
||||||
|
| `Relation` | Referenz zu anderen Records | array |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Fehlerbild-Cheatsheet
|
||||||
|
|
||||||
|
| Symptom | Ursache | Fix |
|
||||||
|
|---|---|---|
|
||||||
|
| Content Block erscheint nicht im CE-Wizard | Cache oder Gruppe nicht registriert | `cache:flush`, dann Backend-User-Session refresh |
|
||||||
|
| Spalten fehlen in DB (`column not found`) | `database:updateschema` nicht ausgeführt | `vendor/bin/typo3 database:updateschema` |
|
||||||
|
| Feld-Label bleibt englisch/identifier | XLF-Key-Konvention falsch | Key muss exakt `<identifier>.label` heißen (ohne Vendor-Prefix) |
|
||||||
|
| JSON enthält veraltete Struktur | `nb-headless-content-blocks` nicht geladen oder Site-Set greift nicht | TypoScript-Analyzer prüfen |
|
||||||
|
| EventListener greift nicht | `Services.yaml` nicht geladen oder `autoconfigure: false` | Services.yaml prüfen, Cache flush |
|
||||||
|
| Icon wird nicht angezeigt | falsche Position oder SVG-Fehler | Pfad muss `assets/icon.svg` relativ zum CB-Ordner sein |
|
||||||
|
| "Type does not exist" nach Rename | TCA-Cache veraltet | `cache:flush --group=system` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Quickstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Aufräumen (einmalig)
|
||||||
|
# - nb-headless-content-blocks ins Site Set migrieren
|
||||||
|
# - Version in ext_emconf.php / composer.json angleichen
|
||||||
|
|
||||||
|
# 2. Ordnerstruktur für ersten Block
|
||||||
|
mkdir -p packages/vitec/ContentBlocks/ContentElements/hero-section/{assets,language,templates}
|
||||||
|
|
||||||
|
# 3. Files anlegen:
|
||||||
|
# - config.yaml
|
||||||
|
# - language/labels.xlf
|
||||||
|
# - assets/icon.svg
|
||||||
|
# - templates/EditorPreview.html (optional)
|
||||||
|
|
||||||
|
# 4. DB + Cache
|
||||||
|
vendor/bin/typo3 database:updateschema
|
||||||
|
vendor/bin/typo3 cache:flush
|
||||||
|
|
||||||
|
# 5. Backend: CE anlegen, speichern
|
||||||
|
|
||||||
|
# 6. Test
|
||||||
|
curl -s https://dev.vitec.com/testseite | jq '.content.colPos0'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Nächste Schritte
|
||||||
|
|
||||||
|
Nachdem der erste Content Block läuft:
|
||||||
|
|
||||||
|
1. **EventListener** für saubere Keys einbauen (Schritt 4)
|
||||||
|
2. Die 20 Templates aus Figma durchgehen → pro Template ein Content Block
|
||||||
|
3. Wenn Layouts mit nested CEs gebraucht werden → `b13/container` dazupacken
|
||||||
|
4. **Custom Records** (`tx_vitec_market`, `_solution`, `_product`, `_story`) — Content Blocks kann auch Record Types. Siehe YAML reference → RecordTypes
|
||||||
|
5. **m:n Relationen** zwischen Records — via `Relation`-Field-Type (mit `allowed` und `maxitems`)
|
||||||
|
|
||||||
|
Sobald der Hero-Section-Block JSON liefert, melden — dann bauen wir zusammen den ersten Record Type (`tx_vitec_market`) und die bidirektionalen Relationen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referenzen
|
||||||
|
|
||||||
|
- **Content Blocks:** https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/
|
||||||
|
- **Field Types:** https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/YamlReference/FieldTypes/Index.html
|
||||||
|
- **nb-headless-content-blocks:** https://github.com/Netzbewegung-Backend/nb_headless_content_blocks
|
||||||
|
- **Headless Docs:** https://docs.typo3.org/p/friendsoftypo3/headless/main/en-us/
|
||||||
|
- **Beispiel-Repo (Content Blocks):** https://github.com/friendsoftypo3/content-blocks/tree/main/Build/content_blocks_examples
|
||||||
@@ -15,11 +15,13 @@
|
|||||||
"sort-packages": true
|
"sort-packages": true
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
"b13/container": "^3.1",
|
||||||
"b13/typo3-updater": "^1.1",
|
"b13/typo3-updater": "^1.1",
|
||||||
"co-stack/logs": "^5.3",
|
"co-stack/logs": "^5.3",
|
||||||
"evomedien/vitec": "^1.0.1",
|
"evomedien/vitec": "^1.0.1",
|
||||||
"friendsoftypo3/content-blocks": "^1.3",
|
"friendsoftypo3/content-blocks": "^1.3",
|
||||||
"friendsoftypo3/headless": "^4.7",
|
"friendsoftypo3/headless": "^4.2",
|
||||||
|
"itplusx/headless-container": "^3.0",
|
||||||
"netzbewegung/nb-headless-content-blocks": "^0.0.21",
|
"netzbewegung/nb-headless-content-blocks": "^0.0.21",
|
||||||
"nitsan/ns-license": "^13.0",
|
"nitsan/ns-license": "^13.0",
|
||||||
"typo3/cms-backend": "^13.4",
|
"typo3/cms-backend": "^13.4",
|
||||||
|
|||||||
494
composer.lock
generated
494
composer.lock
generated
File diff suppressed because it is too large
Load Diff
50
config/sites/vitec/config.yaml
Normal file
50
config/sites/vitec/config.yaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
base: /
|
||||||
|
dependencies:
|
||||||
|
- typo3/fluid-styled-content
|
||||||
|
- typo3/fluid-styled-content-css
|
||||||
|
- typo3/felogin
|
||||||
|
- typo3/form
|
||||||
|
- typo3/seo-sitemap
|
||||||
|
- friendsoftypo3/headless
|
||||||
|
- friendsoftypo3/headless-mixed
|
||||||
|
- itplusx/headless-container
|
||||||
|
- nb-headless-content-blocks/headless-content-blocks
|
||||||
|
- evomedien/vitecset
|
||||||
|
- vitec/content-blocks-bundle
|
||||||
|
- vitec/cta-banner
|
||||||
|
- vitec/herosection
|
||||||
|
frontendBase: ''
|
||||||
|
headless: 1
|
||||||
|
languages:
|
||||||
|
-
|
||||||
|
title: English
|
||||||
|
enabled: true
|
||||||
|
languageId: 0
|
||||||
|
base: /
|
||||||
|
locale: en_US.UTF-8
|
||||||
|
navigationTitle: English
|
||||||
|
flag: us
|
||||||
|
rootPageId: 1
|
||||||
|
routeEnhancers:
|
||||||
|
ProductPlugin:
|
||||||
|
type: Extbase
|
||||||
|
limitToPages:
|
||||||
|
- 10
|
||||||
|
extension: Vitec
|
||||||
|
plugin: Productshow
|
||||||
|
routes:
|
||||||
|
-
|
||||||
|
routePath: '/{product_slug}'
|
||||||
|
_controller: 'Product::show'
|
||||||
|
_arguments:
|
||||||
|
product_slug: product
|
||||||
|
defaultController: 'Product::show'
|
||||||
|
requirements:
|
||||||
|
product_slug: '[a-zA-Z0-9\-]+'
|
||||||
|
aspects:
|
||||||
|
product_slug:
|
||||||
|
type: PersistedAliasMapper
|
||||||
|
tableName: tx_vitec_domain_model_product
|
||||||
|
routeFieldName: slug
|
||||||
|
routeValuePrefix: ''
|
||||||
|
websiteTitle: ''
|
||||||
4
config/sites/vitec/settings.yaml
Normal file
4
config/sites/vitec/settings.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
vitec.debugMode: true
|
||||||
|
my.example.setting: Test
|
||||||
|
menu.footer.pageUids: '1,2,3'
|
||||||
|
menu.meta.pageUids: '5'
|
||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "live",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
55
packages/vitec/.editorconfig
Normal file
55
packages/vitec/.editorconfig
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# TS/JS-Files
|
||||||
|
[*.{ts,js}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# JSON files
|
||||||
|
[*.json]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# package.json
|
||||||
|
[package.json]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# ReST files
|
||||||
|
[*.rst]
|
||||||
|
indent_size = 3
|
||||||
|
max_line_length = 80
|
||||||
|
|
||||||
|
# SQL files
|
||||||
|
[*.sql]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# TypoScript files
|
||||||
|
[*.{typoscript,tsconfig}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# YAML files
|
||||||
|
[{*.yml,*.yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# XLF files
|
||||||
|
[*.xlf]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# .htaccess
|
||||||
|
[.htaccess]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# Markdown files
|
||||||
|
[*.md]
|
||||||
|
max_line_length = 80
|
||||||
19
packages/vitec/Classes/Controller/ComponentCollection.php
Normal file
19
packages/vitec/Classes/Controller/ComponentCollection.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Components;
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use TYPO3Fluid\Fluid\Core\Component\AbstractComponentCollection;
|
||||||
|
use TYPO3Fluid\Fluid\View\TemplatePaths;
|
||||||
|
|
||||||
|
final class ComponentCollection extends AbstractComponentCollection
|
||||||
|
{
|
||||||
|
public function getTemplatePaths(): TemplatePaths
|
||||||
|
{
|
||||||
|
$templatePaths = new TemplatePaths();
|
||||||
|
$templatePaths->setTemplateRootPaths([
|
||||||
|
'EXT:Vitec/Resources/Private/Components/',
|
||||||
|
]);
|
||||||
|
return $templatePaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
445
packages/vitec/Classes/Controller/DownloadController.php
Normal file
445
packages/vitec/Classes/Controller/DownloadController.php
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Core\Environment;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||||
|
use TYPO3\CMS\Core\Database\Connection;
|
||||||
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||||
|
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||||
|
use TYPO3\CMS\Core\Mail\MailerInterface;
|
||||||
|
use TYPO3\CMS\Core\Mail\FluidEmail;
|
||||||
|
use TYPO3\CMS\Core\Http\ResponseFactory;
|
||||||
|
use TYPO3\CMS\Core\Http\Stream;
|
||||||
|
use TYPO3\CMS\Extbase\Http\ForwardResponse;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ResponseFactoryInterface;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\DownloadRepository;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use Evomedien\Vitec\Domain\Model\Download;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DownloadController
|
||||||
|
*/
|
||||||
|
class DownloadController extends ActionController
|
||||||
|
{
|
||||||
|
protected DownloadRepository $downloadRepository;
|
||||||
|
protected ProductRepository $productRepository;
|
||||||
|
protected MailerInterface $mailer;
|
||||||
|
protected ResponseFactoryInterface $responseFactory;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
DownloadRepository $downloadRepository,
|
||||||
|
ProductRepository $productRepository,
|
||||||
|
MailerInterface $mailer,
|
||||||
|
ResponseFactoryInterface $responseFactory
|
||||||
|
) {
|
||||||
|
$this->downloadRepository = $downloadRepository;
|
||||||
|
$this->productRepository = $productRepository;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->responseFactory = $responseFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action list
|
||||||
|
*/
|
||||||
|
public function listAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$downloads = $this->downloadRepository->findAll();
|
||||||
|
$this->view->assign('downloads', $downloads);
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action show
|
||||||
|
*/
|
||||||
|
public function showAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$products = $this->productRepository->findAlphabetically(false, false);
|
||||||
|
$ps = [];
|
||||||
|
$used = [];
|
||||||
|
|
||||||
|
foreach ($products as $p) {
|
||||||
|
if (!in_array($p->getUid(), $used)) {
|
||||||
|
$scat = $p->getProductsubcategory()->toArray();
|
||||||
|
$cat = $p->getProductcategory()->toArray();
|
||||||
|
if (!empty($scat)) {
|
||||||
|
$ps[$scat[0]->getTitle()][] = $p;
|
||||||
|
} elseif (!empty($cat)) {
|
||||||
|
$ps[$cat[0]->getTitle()][] = $p;
|
||||||
|
}
|
||||||
|
$used[] = $p->getUid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($ps);
|
||||||
|
|
||||||
|
$this->view->assign('settings', $this->settings);
|
||||||
|
$this->view->assign('products', $ps);
|
||||||
|
// $dcs = $this->dcRepository->fetchForWebsite();
|
||||||
|
// $this->view->assign('dcs', $dcs);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action directdownload
|
||||||
|
*/
|
||||||
|
public function directdownloadAction(Download $download): ResponseInterface
|
||||||
|
{
|
||||||
|
$json = $download->getForJSON();
|
||||||
|
$file = Environment::getProjectPath() . $json['file'];
|
||||||
|
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
throw new \Exception('File not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->responseFactory->createResponse()
|
||||||
|
->withHeader('Content-Type', 'application/pdf')
|
||||||
|
->withHeader('Content-Disposition', 'inline; filename="' . basename($file) . '"')
|
||||||
|
->withHeader('Content-Transfer-Encoding', 'binary')
|
||||||
|
->withHeader('Accept-Ranges', 'bytes')
|
||||||
|
->withHeader('Content-Length', (string)filesize($file))
|
||||||
|
->withHeader('Pragma', 'public')
|
||||||
|
->withHeader('Expires', '0')
|
||||||
|
->withHeader('Cache-Control', 'must-revalidate');
|
||||||
|
|
||||||
|
$stream = new Stream(fopen($file, 'rb'));
|
||||||
|
return $response->withBody($stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action ac
|
||||||
|
*/
|
||||||
|
public function acAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$downloads = [];
|
||||||
|
$request = $this->request;
|
||||||
|
|
||||||
|
if (!empty($request->getQueryParams()['dc'])) {
|
||||||
|
$downloads = $this->downloadRepository->findByCategory($request->getQueryParams()['dc'], 1);
|
||||||
|
} elseif (!empty($request->getQueryParams()['k'])) {
|
||||||
|
$downloads = $this->downloadRepository->search($request->getQueryParams()['k'], 1);
|
||||||
|
} elseif (!empty($request->getQueryParams()['p'])) {
|
||||||
|
$downloads = $this->downloadRepository->findByProduct($request->getQueryParams()['p'], 1);
|
||||||
|
$product = $this->productRepository->findByUid($request->getQueryParams()['p']);
|
||||||
|
$this->view->assign('product', $product);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->downloadInfo($downloads);
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function downloadInfo($downloads): void
|
||||||
|
{
|
||||||
|
if (!empty($downloads)) {
|
||||||
|
$info = [];
|
||||||
|
foreach ($downloads as $d) {
|
||||||
|
$file = $d->getFile();
|
||||||
|
if (!empty($file)) {
|
||||||
|
$fileResourceReference = GeneralUtility::makeInstance(ResourceFactory::class)
|
||||||
|
->getFileReferenceObject($file->getUid());
|
||||||
|
$file = urldecode($fileResourceReference->getOriginalFile()->getPublicUrl());
|
||||||
|
$i = pathinfo($file);
|
||||||
|
$i['size'] = $this->human_filesize(filesize($file));
|
||||||
|
$info[$d->getUid()] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->view->assign('info', $info);
|
||||||
|
}
|
||||||
|
$this->view->assign('downloads', $downloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function human_filesize($bytes, $decimals = 2): string
|
||||||
|
{
|
||||||
|
$sz = 'BKMGTP';
|
||||||
|
$factor = floor((strlen(strval($bytes)) - 1) / 3);
|
||||||
|
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action download
|
||||||
|
*/
|
||||||
|
public function downloadAction(Download $download): ResponseInterface
|
||||||
|
{
|
||||||
|
return $this->directdownloadAction($download);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function combineddlAction(string $files): ResponseInterface
|
||||||
|
{
|
||||||
|
$ids = explode(',', $files);
|
||||||
|
$downloads = $this->downloadRepository->findByUids($ids);
|
||||||
|
$file = $this->createZip(md5($files), $downloads, null);
|
||||||
|
|
||||||
|
if ($file !== false) {
|
||||||
|
$response = $this->responseFactory->createResponse()
|
||||||
|
->withHeader('Content-disposition', 'attachment; filename=Vitec.zip')
|
||||||
|
->withHeader('Content-type', 'application/zip')
|
||||||
|
->withHeader('Content-Length', (string)filesize($file));
|
||||||
|
|
||||||
|
$stream = new Stream(fopen($file, 'rb'));
|
||||||
|
unlink($file);
|
||||||
|
return $response->withBody($stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action datasheets
|
||||||
|
*/
|
||||||
|
public function datasheetsAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$productso = $this->productRepository->fetchForDatasheets();
|
||||||
|
$sheets = [];
|
||||||
|
$faved = [];
|
||||||
|
$faves = [];
|
||||||
|
$request = $this->request;
|
||||||
|
|
||||||
|
if (!empty($_COOKIE['datasheet-faves'])) {
|
||||||
|
$faved = json_decode($_COOKIE['datasheet-faves']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($request->getParsedBody()['download'])) {
|
||||||
|
$downloads = $this->downloadRepository->findByUids($faved);
|
||||||
|
$file = $this->createZip($_COOKIE['datasheet-faves'], $downloads, null);
|
||||||
|
if ($file !== false) {
|
||||||
|
$response = $this->responseFactory->createResponse()
|
||||||
|
->withHeader('Content-disposition', 'attachment; filename=Vitec.zip')
|
||||||
|
->withHeader('Content-type', 'application/zip')
|
||||||
|
->withHeader('Content-Length', (string)filesize($file));
|
||||||
|
|
||||||
|
$stream = new Stream(fopen($file, 'rb'));
|
||||||
|
unlink($file);
|
||||||
|
return $response->withBody($stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($productso)) {
|
||||||
|
foreach ($productso as $p) {
|
||||||
|
$downloads = $p->getDownload();
|
||||||
|
foreach ($downloads as $d) {
|
||||||
|
$isSheet = false;
|
||||||
|
foreach ($d->getType() as $t) {
|
||||||
|
// filter download type ids
|
||||||
|
if ($t->getUid() == 1 || $t->getUid() == 3) {
|
||||||
|
$isSheet = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($isSheet) {
|
||||||
|
$cat = null;
|
||||||
|
$maincategorysort = 0;
|
||||||
|
foreach ($p->getProductcategory() as $c) {
|
||||||
|
$cat = $c->getTitle();
|
||||||
|
$maincategorysort = $c->getSort3();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subcat = null;
|
||||||
|
$subcatsort = 0;
|
||||||
|
foreach ($p->getProductsubcategory() as $s) {
|
||||||
|
$subcat = $s->getTitle();
|
||||||
|
$subcatsort = $s->getSort3();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$isFaved = false;
|
||||||
|
if (in_array($d->getUid(), $faved)) {
|
||||||
|
$faves[] = [
|
||||||
|
'product' => $p,
|
||||||
|
'datasheet' => $d
|
||||||
|
];
|
||||||
|
$isFaved = true;
|
||||||
|
}
|
||||||
|
$sheets[] = [
|
||||||
|
'isfaved' => $isFaved,
|
||||||
|
'category' => $cat,
|
||||||
|
'sort' => $maincategorysort,
|
||||||
|
'subcategory' => $subcat,
|
||||||
|
'subcategorysort' => strval($subcatsort),
|
||||||
|
'product' => $p,
|
||||||
|
'datasheet' => $d
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($sheets, function ($a, $b) {
|
||||||
|
if ($a["sort"] === $b["sort"]) {
|
||||||
|
if ($a["subcategorysort"] === $b["subcategorysort"]) {
|
||||||
|
return strcmp($a["product"]->getTitle(), $b["product"]->getTitle());
|
||||||
|
} else {
|
||||||
|
return strcmp($a["subcategorysort"], $b["subcategorysort"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strcmp(strval($a["sort"]), strval($b["sort"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->view->assign('sheets', $sheets);
|
||||||
|
$this->view->assign('faves', $faves);
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action index
|
||||||
|
*/
|
||||||
|
public function indexAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createZip($req, $downloads, $stories): string|false
|
||||||
|
{
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$tmp_file = '/tmp/' . sha1($req) . '.zip';
|
||||||
|
|
||||||
|
if ($zip->open($tmp_file, \ZipArchive::CREATE)) {
|
||||||
|
if (!empty($downloads)) {
|
||||||
|
foreach ($downloads as $download) {
|
||||||
|
$f = $download->getFilepfad();
|
||||||
|
if ($f !== false) {
|
||||||
|
$zip->addFile($f, basename($f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($stories)) {
|
||||||
|
foreach ($stories as $story) {
|
||||||
|
$f = $story['pdf'];
|
||||||
|
if ($f !== false) {
|
||||||
|
$zip->addFile($f, basename($f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$zip->close();
|
||||||
|
return $tmp_file;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action collection
|
||||||
|
*/
|
||||||
|
public function collectionAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$downloads = [];
|
||||||
|
$downloadids = [];
|
||||||
|
$stories = [];
|
||||||
|
$storyids = [];
|
||||||
|
$request = $this->request;
|
||||||
|
$parsedBody = $request->getParsedBody();
|
||||||
|
|
||||||
|
if (!empty($parsedBody['downloads'])) {
|
||||||
|
$ids = explode(',', $parsedBody['downloads']);
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$downloadids[] = intval($id);
|
||||||
|
}
|
||||||
|
$downloadids = implode(',', $downloadids);
|
||||||
|
$downloads = $this->downloadRepository->findByUids($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($parsedBody['stories'])) {
|
||||||
|
$ids = explode(',', $parsedBody['stories']);
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$storyids[] = intval($id);
|
||||||
|
}
|
||||||
|
$storyids = implode(',', $storyids);
|
||||||
|
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
$whereExpressions = [
|
||||||
|
$queryBuilder->expr()->eq('colPos', $queryBuilder->createNamedParameter(0)),
|
||||||
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(18)),
|
||||||
|
$queryBuilder->expr()->in(
|
||||||
|
'uid',
|
||||||
|
$queryBuilder->createNamedParameter(
|
||||||
|
explode(',', $storyids),
|
||||||
|
Connection::PARAM_INT_ARRAY
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('mask_use_case')),
|
||||||
|
$queryBuilder->expr()->eq('tx_mask_app_visible', $queryBuilder->createNamedParameter(1))
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $queryBuilder->select('tt_content.*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(...$whereExpressions)
|
||||||
|
->orderBy('tt_content.sorting')
|
||||||
|
->executeQuery();
|
||||||
|
$rows = $result->fetchAllAssociative();
|
||||||
|
|
||||||
|
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
|
||||||
|
|
||||||
|
foreach ($rows as $content) {
|
||||||
|
$pdf_url = '';
|
||||||
|
$filename = '';
|
||||||
|
if (!empty($content['tx_mask_detail_pdf'])) {
|
||||||
|
try {
|
||||||
|
$filename = Environment::getProjectPath() . '/' . $content['tx_mask_detail_pdf'];
|
||||||
|
$pdf_url = $this->uriBuilder->setTargetPageUid(1)->buildFrontendUri() .
|
||||||
|
substr(str_replace(Environment::getProjectPath(), '', $filename), 1);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Handle exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$stories[$content['uid']] = [
|
||||||
|
'uid' => $content['uid'],
|
||||||
|
'header' => $content['header'],
|
||||||
|
'pdf' => $filename,
|
||||||
|
'detail_pdf' => $pdf_url
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($parsedBody['email']) &&
|
||||||
|
filter_var($parsedBody['email'], FILTER_VALIDATE_EMAIL) &&
|
||||||
|
(!empty($parsedBody['downloads']) || !empty($parsedBody['stories']))) {
|
||||||
|
|
||||||
|
$file = $this->createZip($parsedBody['downloads'] . $parsedBody['stories'], $downloads, $stories);
|
||||||
|
if ($file !== false) {
|
||||||
|
$email = GeneralUtility::makeInstance(FluidEmail::class);
|
||||||
|
$email->from('downloads@vitec.com', 'VITEC')
|
||||||
|
->to($parsedBody['email'])
|
||||||
|
->subject('VITEC Datasheets download')
|
||||||
|
->text('Thank you for your interest in VITEC products. Attached, please find your Datasheets ready for download.')
|
||||||
|
->attachFromPath($file, 'Vitec.zip');
|
||||||
|
|
||||||
|
$this->mailer->send($email);
|
||||||
|
|
||||||
|
$redirect = $this->uriBuilder->setTargetPageUid(186)->setCreateAbsoluteUri(true)->buildFrontendUri();
|
||||||
|
return $this->redirectToUri($redirect);
|
||||||
|
}
|
||||||
|
} elseif (!empty($parsedBody['downloads']) || !empty($parsedBody['stories'])) {
|
||||||
|
$file = $this->createZip($parsedBody['downloads'] . $parsedBody['stories'], $downloads, $stories);
|
||||||
|
if ($file !== false) {
|
||||||
|
$response = $this->responseFactory->createResponse()
|
||||||
|
->withHeader('Content-disposition', 'attachment; filename=Vitec.zip')
|
||||||
|
->withHeader('Content-type', 'application/zip')
|
||||||
|
->withHeader('Content-Length', (string)filesize($file));
|
||||||
|
|
||||||
|
$stream = new Stream(fopen($file, 'rb'));
|
||||||
|
unlink($file);
|
||||||
|
return $response->withBody($stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->view->assign('downloads', $downloads);
|
||||||
|
$this->view->assign('downloadids', $downloadids);
|
||||||
|
$this->view->assign('stories', $stories);
|
||||||
|
$this->view->assign('storyids', $storyids);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
60
packages/vitec/Classes/Controller/MarketController.php
Normal file
60
packages/vitec/Classes/Controller/MarketController.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\Domain\Repository\MarketRepository;
|
||||||
|
use Evomedien\Vitec\Domain\Model\Market;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MarketController
|
||||||
|
*/
|
||||||
|
class MarketController extends ActionController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* marketRepository
|
||||||
|
*
|
||||||
|
* @var MarketRepository
|
||||||
|
*/
|
||||||
|
protected $marketRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param MarketRepository $marketRepository
|
||||||
|
*/
|
||||||
|
public function __construct(MarketRepository $marketRepository)
|
||||||
|
{
|
||||||
|
$this->marketRepository = $marketRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action show
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function showAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$selectedMarketId = (int)$this->settings['market'];
|
||||||
|
$market = null;
|
||||||
|
if ($selectedMarketId) {
|
||||||
|
$market = $this->marketRepository->findByUid($selectedMarketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->view->assign('market', $market);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
199
packages/vitec/Classes/Controller/ProductController.php
Normal file
199
packages/vitec/Classes/Controller/ProductController.php
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use Evomedien\Vitec\PageTitle\ProductPageTitleProvider;
|
||||||
|
use TYPO3\CMS\Core\Routing\PageLinkBuilder;
|
||||||
|
use TYPO3\CMS\Core\LinkHandling\LinkService;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductController
|
||||||
|
*/
|
||||||
|
class ProductController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* productRepository
|
||||||
|
*
|
||||||
|
* @var \Evomedien\Vitec\Domain\Repository\ProductRepository
|
||||||
|
*/
|
||||||
|
protected $productRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Evomedien\Vitec\PageTitle\ProductPageTitleProvider
|
||||||
|
*/
|
||||||
|
protected $titleProvider;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Evomedien\Vitec\Domain\Repository\ProductRepository $productRepository
|
||||||
|
*/
|
||||||
|
public function injectProductRepository(\Evomedien\Vitec\Domain\Repository\ProductRepository $productRepository)
|
||||||
|
{
|
||||||
|
$this->productRepository = $productRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject the TitleProvider
|
||||||
|
*
|
||||||
|
* @param \Evomedien\Vitec\PageTitle\ProductPageTitleProvider $titleProvider
|
||||||
|
*/
|
||||||
|
public function injectTitleProvider(ProductPageTitleProvider $titleProvider): void
|
||||||
|
{
|
||||||
|
$this->titleProvider = $titleProvider;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @var LinkService
|
||||||
|
*/
|
||||||
|
protected $linkService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param ProductRepository $productRepository
|
||||||
|
* @param ProductPageTitleProvider $titleProvider
|
||||||
|
* @param LinkService $linkService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
ProductRepository $productRepository,
|
||||||
|
ProductPageTitleProvider $titleProvider,
|
||||||
|
|
||||||
|
) {
|
||||||
|
$this->productRepository = $productRepository;
|
||||||
|
$this->titleProvider = $titleProvider;
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* action index
|
||||||
|
*
|
||||||
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function indexAction(): \Psr\Http\Message\ResponseInterface
|
||||||
|
{
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action list
|
||||||
|
*
|
||||||
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function listAction(): \Psr\Http\Message\ResponseInterface
|
||||||
|
{
|
||||||
|
// Extract and sanitize category UIDs from FlexForm
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($this->settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get filtered products based on categories and visibility flags
|
||||||
|
$products = $this->productRepository->findFilteredProducts($categoryUids);
|
||||||
|
|
||||||
|
$this->view->assign('products', $products);
|
||||||
|
|
||||||
|
// Check if we're in headless mode (JSON request)
|
||||||
|
$request = $this->request;
|
||||||
|
$pageType = $request->getAttribute('routing')?->getPageType() ?? 0;
|
||||||
|
|
||||||
|
if ($pageType === 834) { // Headless JSON page type
|
||||||
|
return $this->jsonResponse(json_encode([
|
||||||
|
'products' => $this->serializeProducts($products)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize products to array for JSON output
|
||||||
|
*/
|
||||||
|
protected function serializeProducts($products): array
|
||||||
|
{
|
||||||
|
$productsData = [];
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$categories = [];
|
||||||
|
if ($product->getCategories()) {
|
||||||
|
foreach ($product->getCategories() as $category) {
|
||||||
|
$categories[] = [
|
||||||
|
'uid' => $category->getUid(),
|
||||||
|
'title' => $category->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = null;
|
||||||
|
if ($product->getProductimage() && $product->getProductimage()->count() > 0) {
|
||||||
|
$imageObject = $product->getProductimage()->current();
|
||||||
|
$image = [
|
||||||
|
'uid' => $imageObject->getUid(),
|
||||||
|
'url' => $imageObject->getOriginalResource()->getPublicUrl(),
|
||||||
|
'title' => $imageObject->getTitle(),
|
||||||
|
'alternative' => $imageObject->getAlternative(),
|
||||||
|
'description' => $imageObject->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => $product->getUid(),
|
||||||
|
'title' => $product->getTitle(),
|
||||||
|
'subtitle' => $product->getSubtitle(),
|
||||||
|
'teaser' => $product->getTeaser(),
|
||||||
|
'description' => $product->getDescription(),
|
||||||
|
'slug' => $product->getSlug(),
|
||||||
|
'categories' => $categories,
|
||||||
|
'image' => $image,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $productsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action show
|
||||||
|
*
|
||||||
|
* @param \Evomedien\Vitec\Domain\Model\Product $product
|
||||||
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function showAction(\Evomedien\Vitec\Domain\Model\Product $product): \Psr\Http\Message\ResponseInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
$metaTagManager = GeneralUtility::makeInstance(MetaTagManagerRegistry::class)->getManagerForProperty('og:title');
|
||||||
|
$metaTagManager->addProperty('og:title', ($product->getSeotitle()));
|
||||||
|
$metaTagManager->addProperty('og:description', ($product->getDescription()));
|
||||||
|
//$metaTagManager->addProperty('og:url', $this->request->getRequestUri());
|
||||||
|
$metaTagManager->addProperty('og:type', 'product');
|
||||||
|
|
||||||
|
if ($product->getOgimage()) {
|
||||||
|
$ogImageUrl = $product->getOgimage()->getOriginalResource()->getPublicUrl();
|
||||||
|
$metaTagManager->addProperty('og:image', $ogImageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access the layout setting from FlexForm
|
||||||
|
$layout = $this->settings['layout'] ?? 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Pass the layout to the view
|
||||||
|
$this->view->assign('layout', $layout);
|
||||||
|
|
||||||
|
$this->view->assign('product', $product);
|
||||||
|
$this->titleProvider->setSeoTitle($product->getSeotitle());
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
packages/vitec/Classes/Controller/SimplecardController.php
Normal file
12
packages/vitec/Classes/Controller/SimplecardController.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
|
||||||
|
class SimplecardController extends ActionController
|
||||||
|
{
|
||||||
|
public function listAction(): \Psr\Http\Message\ResponseInterface
|
||||||
|
{
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
60
packages/vitec/Classes/Controller/SolutionController.php
Normal file
60
packages/vitec/Classes/Controller/SolutionController.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\Domain\Repository\SolutionRepository;
|
||||||
|
use Evomedien\Vitec\Domain\Model\Solution;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SolutionController
|
||||||
|
*/
|
||||||
|
class SolutionController extends ActionController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* solutionRepository
|
||||||
|
*
|
||||||
|
* @var SolutionRepository
|
||||||
|
*/
|
||||||
|
protected $solutionRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param SolutionRepository $solutionRepository
|
||||||
|
*/
|
||||||
|
public function __construct(SolutionRepository $solutionRepository)
|
||||||
|
{
|
||||||
|
$this->solutionRepository = $solutionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action show
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function showAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$selectedSolutionId = (int)$this->settings['solution'];
|
||||||
|
$solution = null;
|
||||||
|
if ($selectedSolutionId) {
|
||||||
|
$solution = $this->solutionRepository->findByUid($selectedSolutionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->view->assign('solution', $solution);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
75
packages/vitec/Classes/Controller/UsecaseController.php
Normal file
75
packages/vitec/Classes/Controller/UsecaseController.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Controller;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\Domain\Repository\UsecaseRepository;
|
||||||
|
use Evomedien\Vitec\Domain\Model\Usecase;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UsecaseController
|
||||||
|
*/
|
||||||
|
class UsecaseController extends ActionController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* usecaseRepository
|
||||||
|
*
|
||||||
|
* @var UsecaseRepository
|
||||||
|
*/
|
||||||
|
protected $usecaseRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param UsecaseRepository $usecaseRepository
|
||||||
|
*/
|
||||||
|
public function __construct(UsecaseRepository $usecaseRepository)
|
||||||
|
{
|
||||||
|
$this->usecaseRepository = $usecaseRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action show
|
||||||
|
*
|
||||||
|
* @param Usecase $usecase
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function showAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$selectedUsecaseId = (int)$this->settings['usecase']; // Die ID aus dem Flexform
|
||||||
|
$usecase = null;
|
||||||
|
if ($selectedUsecaseId) {
|
||||||
|
$usecase = $this->usecaseRepository->findByUid($selectedUsecaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->view->assign('usecase', $usecase);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* action list
|
||||||
|
*
|
||||||
|
* @param Usecase $usecase
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function listAction(): ResponseInterface
|
||||||
|
{
|
||||||
|
$usecases = $this->usecaseRepository->findAllWithCategories();
|
||||||
|
$this->view->assign('usecases', $usecases);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
157
packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php
Executable file
157
packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php
Executable file
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\DataProcessing;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Database\Connection;
|
||||||
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect container children grouped by colPos and emit a lean, transport-
|
||||||
|
* ready structure. Each child is normalised to:
|
||||||
|
* { id, type, colPos, sorting, appearance, data }
|
||||||
|
* `data` only contains non-empty content-relevant fields.
|
||||||
|
*
|
||||||
|
* Exception-safe.
|
||||||
|
*/
|
||||||
|
final class ContainerChildrenProcessor implements DataProcessorInterface
|
||||||
|
{
|
||||||
|
/** Fields that go to the envelope (not to `data`). */
|
||||||
|
private const ENVELOPE = [
|
||||||
|
'uid', 'CType', 'colPos', 'sorting',
|
||||||
|
'layout', 'frame_class', 'space_before_class', 'space_after_class',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Technical / system / TCA-default fields — never sent to frontend. */
|
||||||
|
private const SYSTEM_FIELDS = [
|
||||||
|
// versioning / language / workspace / housekeeping
|
||||||
|
'pid', 'sys_language_uid', 'l18n_parent', 'l18n_diffsource',
|
||||||
|
'l10n_source', 'l10n_state', 'l10n_parent',
|
||||||
|
't3_origuid', 'tx_impexp_origuid',
|
||||||
|
'tx_container_parent',
|
||||||
|
'tstamp', 'crdate', 'cruser_id',
|
||||||
|
'hidden', 'deleted', 'starttime', 'endtime', 'fe_group',
|
||||||
|
't3ver_oid', 't3ver_wsid', 't3ver_state', 't3ver_stage',
|
||||||
|
't3ver_id', 't3ver_label', 't3ver_count', 't3ver_tstamp',
|
||||||
|
'editlock', 'sorting_foreign', 'rowDescription',
|
||||||
|
'spaceBefore', 'spaceAfter', // legacy
|
||||||
|
// TCA defaults that TYPO3 always sets on every tt_content row,
|
||||||
|
// regardless of CType — rarely relevant to the frontend:
|
||||||
|
'imagecols', 'sectionIndex', 'linkToTop', 'recursive', 'date',
|
||||||
|
'bullets_type', 'cols',
|
||||||
|
'table_delimiter', 'table_enclosure', 'table_header_position',
|
||||||
|
'table_tfoot', 'table_caption',
|
||||||
|
'filelink_size', 'filelink_sorting', 'filelink_sorting_direction',
|
||||||
|
'uploads_description', 'uploads_type',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Fields whose 0/empty value is still meaningful. */
|
||||||
|
private const KEEP_IF_ZERO = [
|
||||||
|
'header_layout',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function process(
|
||||||
|
ContentObjectRenderer $cObj,
|
||||||
|
array $contentObjectConfiguration,
|
||||||
|
array $processorConfiguration,
|
||||||
|
array $processedData
|
||||||
|
): array {
|
||||||
|
$as = (string)($processorConfiguration['as'] ?? 'items');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$parentUid = (int)($cObj->data['uid'] ?? 0);
|
||||||
|
if ($parentUid <= 0) {
|
||||||
|
$processedData[$as] = [];
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pid = (int)($cObj->data['pid'] ?? 0);
|
||||||
|
$sysLanguageUid = (int)($cObj->data['sys_language_uid'] ?? 0);
|
||||||
|
|
||||||
|
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
$rows = $qb
|
||||||
|
->select('*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('tx_container_parent', $qb->createNamedParameter($parentUid, Connection::PARAM_INT)),
|
||||||
|
$qb->expr()->eq('pid', $qb->createNamedParameter($pid, Connection::PARAM_INT)),
|
||||||
|
$qb->expr()->eq('sys_language_uid', $qb->createNamedParameter($sysLanguageUid, Connection::PARAM_INT))
|
||||||
|
)
|
||||||
|
->orderBy('colPos')
|
||||||
|
->addOrderBy('sorting')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$byColPos = [];
|
||||||
|
foreach ($rows as $record) {
|
||||||
|
$byColPos[(int)$record['colPos']][] = $this->normalise($record);
|
||||||
|
}
|
||||||
|
ksort($byColPos);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
foreach ($byColPos as $colPos => $contentElements) {
|
||||||
|
$items[] = [
|
||||||
|
'config' => ['colPos' => $colPos],
|
||||||
|
'contentElements' => $contentElements,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$processedData[$as] = $items;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$processedData[$as] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalise(array $record): array
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
foreach ($record as $field => $value) {
|
||||||
|
if (in_array($field, self::ENVELOPE, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (in_array($field, self::SYSTEM_FIELDS, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($this->isEmpty($value) && !in_array($field, self::KEEP_IF_ZERO, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$data[$field] = $this->castValue($field, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => (int)$record['uid'],
|
||||||
|
'type' => (string)$record['CType'],
|
||||||
|
'colPos' => (int)$record['colPos'],
|
||||||
|
'sorting' => (int)($record['sorting'] ?? 0),
|
||||||
|
'appearance' => [
|
||||||
|
'layout' => (string)($record['layout'] ?? ''),
|
||||||
|
'frameClass' => (string)($record['frame_class'] ?? 'default'),
|
||||||
|
'spaceBefore' => (string)($record['space_before_class'] ?? ''),
|
||||||
|
'spaceAfter' => (string)($record['space_after_class'] ?? ''),
|
||||||
|
],
|
||||||
|
'data' => (object)$data,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isEmpty(mixed $value): bool
|
||||||
|
{
|
||||||
|
return $value === null
|
||||||
|
|| $value === ''
|
||||||
|
|| $value === 0
|
||||||
|
|| $value === '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function castValue(string $field, mixed $value): mixed
|
||||||
|
{
|
||||||
|
if (in_array($field, ['header_layout'], true)) {
|
||||||
|
return (int)$value;
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\DataProcessing;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\Domain\Model\Dto\ProductListContentElement;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data processor for vitec_productlist content element
|
||||||
|
*/
|
||||||
|
class ProductListProcessor implements DataProcessorInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected readonly ProductRepository $productRepository,
|
||||||
|
protected readonly FlexFormService $flexFormService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(
|
||||||
|
ContentObjectRenderer $cObj,
|
||||||
|
array $contentObjectConfiguration,
|
||||||
|
array $processorConfiguration,
|
||||||
|
array $processedData
|
||||||
|
): array {
|
||||||
|
// Only process if this is a vitec_productlist
|
||||||
|
if (($processedData['data']['list_type'] ?? '') !== 'vitec_productlist') {
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get FlexForm settings
|
||||||
|
$flexFormData = $this->flexFormService->convertFlexFormContentToArray(
|
||||||
|
$processedData['data']['pi_flexform'] ?? ''
|
||||||
|
);
|
||||||
|
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Extract and sanitize category UIDs from FlexForm
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get filtered products based on categories
|
||||||
|
$products = $this->productRepository->findFilteredProducts($categoryUids);
|
||||||
|
|
||||||
|
// Serialize products directly
|
||||||
|
$productsData = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$categories = [];
|
||||||
|
if ($product->getCategories()) {
|
||||||
|
foreach ($product->getCategories() as $category) {
|
||||||
|
$categories[] = [
|
||||||
|
'uid' => $category->getUid(),
|
||||||
|
'title' => $category->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = null;
|
||||||
|
if ($product->getProductimage() && $product->getProductimage()->count() > 0) {
|
||||||
|
$imageObject = $product->getProductimage()->current();
|
||||||
|
$image = [
|
||||||
|
'uid' => $imageObject->getUid(),
|
||||||
|
'url' => $imageObject->getOriginalResource()->getPublicUrl(),
|
||||||
|
'title' => $imageObject->getTitle(),
|
||||||
|
'alternative' => $imageObject->getAlternative(),
|
||||||
|
'description' => $imageObject->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => $product->getUid(),
|
||||||
|
'title' => $product->getTitle(),
|
||||||
|
'subtitle' => $product->getSubtitle(),
|
||||||
|
'teaser' => $product->getTeaser(),
|
||||||
|
'description' => $product->getDescription(),
|
||||||
|
'slug' => $product->getSlug(),
|
||||||
|
'categories' => $categories,
|
||||||
|
'image' => $image,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$processedData['products'] = $productsData;
|
||||||
|
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\DataProcessing;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple and direct data processor for vitec_productlist
|
||||||
|
*/
|
||||||
|
class VitecProductProcessor implements DataProcessorInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected readonly ProductRepository $productRepository,
|
||||||
|
protected readonly FlexFormService $flexFormService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(
|
||||||
|
ContentObjectRenderer $cObj,
|
||||||
|
array $contentObjectConfiguration,
|
||||||
|
array $processorConfiguration,
|
||||||
|
array $processedData
|
||||||
|
): array {
|
||||||
|
// Only process vitec_productlist
|
||||||
|
if (($processedData['data']['list_type'] ?? '') !== 'vitec_productlist') {
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get FlexForm settings
|
||||||
|
$flexFormData = $this->flexFormService->convertFlexFormContentToArray(
|
||||||
|
$processedData['data']['pi_flexform'] ?? ''
|
||||||
|
);
|
||||||
|
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Extract category UIDs
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get products
|
||||||
|
$products = $this->productRepository->findFilteredProducts($categoryUids);
|
||||||
|
|
||||||
|
// Serialize products
|
||||||
|
$productsData = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => $product->getUid(),
|
||||||
|
'title' => $product->getTitle(),
|
||||||
|
'slug' => $product->getSlug(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to processedData at content level
|
||||||
|
if (!isset($processedData['content'])) {
|
||||||
|
$processedData['content'] = [];
|
||||||
|
}
|
||||||
|
$processedData['content']['products'] = $productsData;
|
||||||
|
|
||||||
|
return $processedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
packages/vitec/Classes/Domain/Model/Category.php
Normal file
29
packages/vitec/Classes/Domain/Model/Category.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\Category as ExtbaseCategory;
|
||||||
|
|
||||||
|
class Category extends ExtbaseCategory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getClass(): string
|
||||||
|
{
|
||||||
|
return $this->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
*/
|
||||||
|
public function setClass(string $class): void
|
||||||
|
{
|
||||||
|
$this->class = $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
packages/vitec/Classes/Domain/Model/Download.php
Normal file
55
packages/vitec/Classes/Domain/Model/Download.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||||
|
|
||||||
|
class Download extends AbstractEntity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $title = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $slug = '';
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $title
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title): void
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSlug(): string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $slug
|
||||||
|
*/
|
||||||
|
public function setSlug(string $slug): void
|
||||||
|
{
|
||||||
|
$this->slug = $slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model\Dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO for Product List Content Element in JSON output
|
||||||
|
*/
|
||||||
|
class ProductListContentElement implements \JsonSerializable
|
||||||
|
{
|
||||||
|
protected array $products = [];
|
||||||
|
protected array $settings = [];
|
||||||
|
|
||||||
|
public function setProducts(array $products): void
|
||||||
|
{
|
||||||
|
$this->products = $products;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSettings(array $settings): void
|
||||||
|
{
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
$productsData = [];
|
||||||
|
|
||||||
|
foreach ($this->products as $product) {
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => $product->getUid(),
|
||||||
|
'title' => $product->getTitle(),
|
||||||
|
'subtitle' => $product->getSubtitle(),
|
||||||
|
'teaser' => $product->getTeaser(),
|
||||||
|
'description' => $product->getDescription(),
|
||||||
|
'slug' => $product->getSlug(),
|
||||||
|
'categories' => $this->serializeCategories($product->getCategories()),
|
||||||
|
'image' => $this->serializeImage($product->getProductimage()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'products' => $productsData,
|
||||||
|
'settings' => $this->settings,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function serializeCategories($categories): array
|
||||||
|
{
|
||||||
|
$categoriesData = [];
|
||||||
|
|
||||||
|
if ($categories) {
|
||||||
|
foreach ($categories as $category) {
|
||||||
|
$categoriesData[] = [
|
||||||
|
'uid' => $category->getUid(),
|
||||||
|
'title' => $category->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $categoriesData;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function serializeImage($images): ?array
|
||||||
|
{
|
||||||
|
if (!$images || $images->count() === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$imageObject = $images->current();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'uid' => $imageObject->getUid(),
|
||||||
|
'url' => $imageObject->getOriginalResource()->getPublicUrl(),
|
||||||
|
'title' => $imageObject->getTitle(),
|
||||||
|
'alternative' => $imageObject->getAlternative(),
|
||||||
|
'description' => $imageObject->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
163
packages/vitec/Classes/Domain/Model/Market.php
Normal file
163
packages/vitec/Classes/Domain/Model/Market.php
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\Category;
|
||||||
|
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||||
|
|
||||||
|
class Market extends AbstractEntity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $title = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $subtitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $teaser = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $image = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\Category>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
|
||||||
|
*/
|
||||||
|
protected $categories;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->categories = new ObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
/* Getter und Setter */
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $title
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title): void
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSubtitle(): string
|
||||||
|
{
|
||||||
|
return $this->subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $subtitle
|
||||||
|
*/
|
||||||
|
public function setSubtitle(string $subtitle): void
|
||||||
|
{
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTeaser(): string
|
||||||
|
{
|
||||||
|
return $this->teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $teaser
|
||||||
|
*/
|
||||||
|
public function setTeaser(string $teaser): void
|
||||||
|
{
|
||||||
|
$this->teaser = $teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $description
|
||||||
|
*/
|
||||||
|
public function setDescription(string $description): void
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \TYPO3\CMS\Extbase\Domain\Model\FileReference|null
|
||||||
|
*/
|
||||||
|
public function getImage()
|
||||||
|
{
|
||||||
|
return $this->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
|
||||||
|
*/
|
||||||
|
public function setImage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $image): void
|
||||||
|
{
|
||||||
|
$this->image = $image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $category
|
||||||
|
*/
|
||||||
|
public function addCategory(Category $category): void
|
||||||
|
{
|
||||||
|
$this->categories->attach($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $categoryToRemove
|
||||||
|
*/
|
||||||
|
public function removeCategory(Category $categoryToRemove): void
|
||||||
|
{
|
||||||
|
$this->categories->detach($categoryToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ObjectStorage<Category>
|
||||||
|
*/
|
||||||
|
public function getCategories(): ObjectStorage
|
||||||
|
{
|
||||||
|
return $this->categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectStorage<Category> $categories
|
||||||
|
*/
|
||||||
|
public function setCategories(ObjectStorage $categories): void
|
||||||
|
{
|
||||||
|
$this->categories = $categories;
|
||||||
|
}
|
||||||
|
}
|
||||||
922
packages/vitec/Classes/Domain/Model/Product.php
Normal file
922
packages/vitec/Classes/Domain/Model/Product.php
Normal file
@@ -0,0 +1,922 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\Category;
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product
|
||||||
|
*/
|
||||||
|
class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* title
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
|
||||||
|
*/
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* slug
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $slug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* urltitle
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $urltitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* seotitle
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $seotitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* seometa
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $seometa = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keywords
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $keywords = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* structureddata
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $structureddata = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* teaser
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $teaser = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subtitle
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $subtitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* video
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $video = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideonapp
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideonapp = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideonwebsite
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideonwebsite = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideondatasheets
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideondatasheets = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideonproducts
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideonproducts = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* applications
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $applications = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* highlights
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $highlights = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shortcut
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $shortcut = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shortcutpid
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $shortcutpid = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* legacy
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $legacy = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* supportproduct
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $supportproduct = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subproduct
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $subproduct = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectStorage<Category>
|
||||||
|
*/
|
||||||
|
protected $categories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product images
|
||||||
|
*
|
||||||
|
* @var ObjectStorage<FileReference>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $productimage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads
|
||||||
|
*
|
||||||
|
* @var ObjectStorage<\Evomedien\Vitec\Domain\Model\Download>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $downloads;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open Graph Image
|
||||||
|
*
|
||||||
|
* @var FileReference
|
||||||
|
*/
|
||||||
|
protected $ogimage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content element link - Key Features
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $contentelement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content element link - CTA Features
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $contentelementcta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Evomedien\Vitec\Domain\Model\Product>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $relatedprodukt;
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the title
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the title
|
||||||
|
*
|
||||||
|
* @param string $title
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title)
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the video
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getVideo()
|
||||||
|
{
|
||||||
|
return $this->video;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the video
|
||||||
|
*
|
||||||
|
* @param string $video
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setVideo(string $video)
|
||||||
|
{
|
||||||
|
$this->video = $video;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slug
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSlug()
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the slug
|
||||||
|
*
|
||||||
|
* @param string $slug
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSlug(string $slug)
|
||||||
|
{
|
||||||
|
$this->slug = $slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the urltitle
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUrltitle()
|
||||||
|
{
|
||||||
|
return $this->urltitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the urltitle
|
||||||
|
*
|
||||||
|
* @param string $urltitle
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUrltitle(string $urltitle)
|
||||||
|
{
|
||||||
|
$this->urltitle = $urltitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the seotitle
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSeotitle()
|
||||||
|
{
|
||||||
|
return $this->seotitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the seotitle
|
||||||
|
*
|
||||||
|
* @param string $seotitle
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSeotitle(string $seotitle)
|
||||||
|
{
|
||||||
|
$this->seotitle = $seotitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the seometa
|
||||||
|
*
|
||||||
|
* @return string $seometa
|
||||||
|
*/
|
||||||
|
public function getSeometa()
|
||||||
|
{
|
||||||
|
return $this->seometa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the seometa
|
||||||
|
*
|
||||||
|
* @param string $seometa
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSeometa($seometa)
|
||||||
|
{
|
||||||
|
$this->seometa = $seometa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the keywords
|
||||||
|
*
|
||||||
|
* @return string $keywords
|
||||||
|
*/
|
||||||
|
public function getKeywords()
|
||||||
|
{
|
||||||
|
return $this->keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the keywords
|
||||||
|
*
|
||||||
|
* @param string $keywords
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setKeywords($keywords)
|
||||||
|
{
|
||||||
|
$this->keywords = $keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the teaser
|
||||||
|
*
|
||||||
|
* @return string $kteasereywords
|
||||||
|
*/
|
||||||
|
public function getTeaser()
|
||||||
|
{
|
||||||
|
return $this->teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the teaser
|
||||||
|
*
|
||||||
|
* @param string $teaser
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTeaser($teaser)
|
||||||
|
{
|
||||||
|
$this->teaser = $teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subtitle
|
||||||
|
*
|
||||||
|
* @return string $subtitle
|
||||||
|
*/
|
||||||
|
public function getSubtitle()
|
||||||
|
{
|
||||||
|
return $this->subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subtitle
|
||||||
|
*
|
||||||
|
* @param string $subtitle
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSubtitle($subtitle)
|
||||||
|
{
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideonapp
|
||||||
|
*
|
||||||
|
* @return bool $hideonapp
|
||||||
|
*/
|
||||||
|
public function getHideonapp()
|
||||||
|
{
|
||||||
|
return $this->hideonapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideonapp
|
||||||
|
*
|
||||||
|
* @param bool $hideonapp
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideonapp($hideonapp)
|
||||||
|
{
|
||||||
|
$this->hideonapp = $hideonapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the description
|
||||||
|
*
|
||||||
|
* @return bool $description
|
||||||
|
*/
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the description
|
||||||
|
*
|
||||||
|
* @param bool $description
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setDescriptionp($description)
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideonwebsite
|
||||||
|
*
|
||||||
|
* @return bool $hideonwebsite
|
||||||
|
*/
|
||||||
|
public function getHideonwebsite()
|
||||||
|
{
|
||||||
|
return $this->hideonwebsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideonwebsite
|
||||||
|
*
|
||||||
|
* @param bool $hideonwebsite
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideonwebsite($hideonwebsite)
|
||||||
|
{
|
||||||
|
$this->hideonwebsite = $hideonwebsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideondatasheets
|
||||||
|
*
|
||||||
|
* @return bool $hideondatasheets
|
||||||
|
*/
|
||||||
|
public function getHideondatasheets()
|
||||||
|
{
|
||||||
|
return $this->hideondatasheets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideondatasheets
|
||||||
|
*
|
||||||
|
* @param bool $hideondatasheets
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideondatasheets($hideondatasheets)
|
||||||
|
{
|
||||||
|
$this->hideondatasheets = $hideondatasheets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideonproducts
|
||||||
|
*
|
||||||
|
* @return bool $hideonproducts
|
||||||
|
*/
|
||||||
|
public function getHideonproducts()
|
||||||
|
{
|
||||||
|
return $this->hideonproducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideonproducts
|
||||||
|
*
|
||||||
|
* @param bool $hideonproducts
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideonproducts($hideonproducts)
|
||||||
|
{
|
||||||
|
$this->hideonproducts = $hideonproducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the structureddata
|
||||||
|
*
|
||||||
|
* @return string $structureddata
|
||||||
|
*/
|
||||||
|
public function getStructureddata()
|
||||||
|
{
|
||||||
|
return $this->structureddata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the structureddata
|
||||||
|
*
|
||||||
|
* @param string $structureddata
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setStructureddata($structureddata)
|
||||||
|
{
|
||||||
|
$this->structureddata = $structureddata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the applications
|
||||||
|
*
|
||||||
|
* @return string $applications
|
||||||
|
*/
|
||||||
|
public function getApplications()
|
||||||
|
{
|
||||||
|
return $this->applications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the applications
|
||||||
|
*
|
||||||
|
* @param string $applications
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApplications($applications)
|
||||||
|
{
|
||||||
|
$this->applications = $applications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highlights
|
||||||
|
*
|
||||||
|
* @return string $highlights
|
||||||
|
*/
|
||||||
|
public function getHighlights()
|
||||||
|
{
|
||||||
|
return $this->highlights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the highlights
|
||||||
|
*
|
||||||
|
* @param string $highlights
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHighlights($highlights)
|
||||||
|
{
|
||||||
|
$this->highlights = $highlights;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the shortcutpid
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getShortcutpid()
|
||||||
|
{
|
||||||
|
return $this->shortcutpid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shortcutpid
|
||||||
|
*
|
||||||
|
* @param string $shortcutpid
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setShortcutpid ($shortcutpid)
|
||||||
|
{
|
||||||
|
$this->shortcutpid = $shortcutpid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the shortcut
|
||||||
|
*
|
||||||
|
* @return bool $shortcut
|
||||||
|
*/
|
||||||
|
public function getShortcut()
|
||||||
|
{
|
||||||
|
return $this->shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shortcut
|
||||||
|
*
|
||||||
|
* @param bool $shortcut
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setShortcut($shortcut)
|
||||||
|
{
|
||||||
|
$this->shortcut = $shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the legacy
|
||||||
|
*
|
||||||
|
* @return bool $legacy
|
||||||
|
*/
|
||||||
|
public function getLegacy()
|
||||||
|
{
|
||||||
|
return $this->legacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the legacy
|
||||||
|
*
|
||||||
|
* @param bool $legacy
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setLegacy($legacy)
|
||||||
|
{
|
||||||
|
$this->legacy = $legacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the supportproduct
|
||||||
|
*
|
||||||
|
* @return bool $supportproduct
|
||||||
|
*/
|
||||||
|
public function getSupportproduct()
|
||||||
|
{
|
||||||
|
return $this->supportproduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the supportproduct
|
||||||
|
*
|
||||||
|
* @param bool $supportproduct
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSupportproduct($supportproduct)
|
||||||
|
{
|
||||||
|
$this->supportproduct = $supportproduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subproduct
|
||||||
|
*
|
||||||
|
* @return bool $subproduct
|
||||||
|
*/
|
||||||
|
public function getSubproduct()
|
||||||
|
{
|
||||||
|
return $this->subproduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subproduct
|
||||||
|
*
|
||||||
|
* @param bool $subproduct
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSubproduct($subproduct)
|
||||||
|
{
|
||||||
|
$this->subproduct = $subproduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the ObjectStorage for categories
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->categories = new ObjectStorage();
|
||||||
|
$this->productimage = new ObjectStorage();
|
||||||
|
$this->downloads = new ObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a category
|
||||||
|
*
|
||||||
|
* @param Category $category
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addCategory(Category $category)
|
||||||
|
{
|
||||||
|
$this->categories->attach($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a category
|
||||||
|
*
|
||||||
|
* @param Category $categoryToRemove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeCategory(Category $categoryToRemove)
|
||||||
|
{
|
||||||
|
$this->categories->detach($categoryToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the categories
|
||||||
|
*
|
||||||
|
* @return ObjectStorage<Category>
|
||||||
|
*/
|
||||||
|
public function getCategories()
|
||||||
|
{
|
||||||
|
return $this->categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the categories
|
||||||
|
*
|
||||||
|
* @param ObjectStorage<Category> $categories
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCategories(ObjectStorage $categories)
|
||||||
|
{
|
||||||
|
$this->categories = $categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a product image
|
||||||
|
*
|
||||||
|
* @param FileReference $productimage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addProductimage(FileReference $productimage)
|
||||||
|
{
|
||||||
|
$this->productimage->attach($productimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a product image
|
||||||
|
*
|
||||||
|
* @param FileReference $productimageToRemove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeProductimage(FileReference $productimageToRemove)
|
||||||
|
{
|
||||||
|
$this->productimage->detach($productimageToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the product images
|
||||||
|
*
|
||||||
|
* @return ObjectStorage<FileReference>
|
||||||
|
*/
|
||||||
|
public function getProductimage()
|
||||||
|
{
|
||||||
|
return $this->productimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the product images
|
||||||
|
*
|
||||||
|
* @param ObjectStorage<FileReference> $productimage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setProductimage(ObjectStorage $productimage)
|
||||||
|
{
|
||||||
|
$this->productimage = $productimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a download
|
||||||
|
*
|
||||||
|
* @param \Evomedien\Vitec\Domain\Model\Download $download
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addDownload(\Evomedien\Vitec\Domain\Model\Download $download): void
|
||||||
|
{
|
||||||
|
$this->downloads->attach($download);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a download
|
||||||
|
*
|
||||||
|
* @param \Evomedien\Vitec\Domain\Model\Download $downloadToRemove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeDownload(\Evomedien\Vitec\Domain\Model\Download $downloadToRemove): void
|
||||||
|
{
|
||||||
|
$this->downloads->detach($downloadToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the downloads
|
||||||
|
*
|
||||||
|
* @return ObjectStorage<\Evomedien\Vitec\Domain\Model\Download>
|
||||||
|
*/
|
||||||
|
public function getDownloads(): ObjectStorage
|
||||||
|
{
|
||||||
|
return $this->downloads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the downloads
|
||||||
|
*
|
||||||
|
* @param ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> $downloads
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setDownloads(ObjectStorage $downloads): void
|
||||||
|
{
|
||||||
|
$this->downloads = $downloads;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Open Graph Image
|
||||||
|
*
|
||||||
|
* @return FileReference|null
|
||||||
|
*/
|
||||||
|
public function getOgimage(): ?FileReference
|
||||||
|
{
|
||||||
|
return $this->ogimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Open Graph Image
|
||||||
|
*
|
||||||
|
* @param FileReference $ogimage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setOgimage(FileReference $ogimage): void
|
||||||
|
{
|
||||||
|
$this->ogimage = $ogimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content element link
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentelement(): string
|
||||||
|
{
|
||||||
|
return $this->contentelement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content element link
|
||||||
|
*
|
||||||
|
* @param string $contentelement
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setContentelement(string $contentelement): void
|
||||||
|
{
|
||||||
|
$this->contentelement = $contentelement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content element cta link
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentelementcta(): string
|
||||||
|
{
|
||||||
|
return $this->contentelementcta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content element link cta
|
||||||
|
*
|
||||||
|
* @param string $contentelementcta
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setContentelementcta(string $contentelementcta): void
|
||||||
|
{
|
||||||
|
$this->contentelementcta = $contentelementcta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product>
|
||||||
|
*/
|
||||||
|
public function getRelatedprodukt(): \TYPO3\CMS\Extbase\Persistence\ObjectStorage
|
||||||
|
{
|
||||||
|
return $this->relatedprodukt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product> $relatedprodukt
|
||||||
|
*/
|
||||||
|
public function setRelatedprodukt(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $relatedprodukt): void
|
||||||
|
{
|
||||||
|
$this->relatedprodukt = $relatedprodukt;
|
||||||
|
}
|
||||||
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
}
|
||||||
163
packages/vitec/Classes/Domain/Model/Solution.php
Normal file
163
packages/vitec/Classes/Domain/Model/Solution.php
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\Category;
|
||||||
|
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||||
|
|
||||||
|
class Solution extends AbstractEntity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $title = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $subtitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $teaser = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $image = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\Category>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
|
||||||
|
*/
|
||||||
|
protected $categories;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->categories = new ObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
/* Getter und Setter */
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $title
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title): void
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSubtitle(): string
|
||||||
|
{
|
||||||
|
return $this->subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $subtitle
|
||||||
|
*/
|
||||||
|
public function setSubtitle(string $subtitle): void
|
||||||
|
{
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTeaser(): string
|
||||||
|
{
|
||||||
|
return $this->teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $teaser
|
||||||
|
*/
|
||||||
|
public function setTeaser(string $teaser): void
|
||||||
|
{
|
||||||
|
$this->teaser = $teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $description
|
||||||
|
*/
|
||||||
|
public function setDescription(string $description): void
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \TYPO3\CMS\Extbase\Domain\Model\FileReference|null
|
||||||
|
*/
|
||||||
|
public function getImage()
|
||||||
|
{
|
||||||
|
return $this->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
|
||||||
|
*/
|
||||||
|
public function setImage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $image): void
|
||||||
|
{
|
||||||
|
$this->image = $image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $category
|
||||||
|
*/
|
||||||
|
public function addCategory(Category $category): void
|
||||||
|
{
|
||||||
|
$this->categories->attach($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $categoryToRemove
|
||||||
|
*/
|
||||||
|
public function removeCategory(Category $categoryToRemove): void
|
||||||
|
{
|
||||||
|
$this->categories->detach($categoryToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ObjectStorage<Category>
|
||||||
|
*/
|
||||||
|
public function getCategories(): ObjectStorage
|
||||||
|
{
|
||||||
|
return $this->categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectStorage<Category> $categories
|
||||||
|
*/
|
||||||
|
public function setCategories(ObjectStorage $categories): void
|
||||||
|
{
|
||||||
|
$this->categories = $categories;
|
||||||
|
}
|
||||||
|
}
|
||||||
335
packages/vitec/Classes/Domain/Model/Usecase.php
Normal file
335
packages/vitec/Classes/Domain/Model/Usecase.php
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Model;
|
||||||
|
use TYPO3\CMS\Extbase\Domain\Model\Category;
|
||||||
|
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||||
|
|
||||||
|
class Usecase extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $title = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $slug = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subtitle
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $subtitle = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* teaser
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $teaser = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideonapp
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideonapp = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hideonwebsite
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hideonwebsite = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $singlepid = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* caseimage
|
||||||
|
*
|
||||||
|
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade
|
||||||
|
*/
|
||||||
|
protected $caseimage = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* logoimage
|
||||||
|
*
|
||||||
|
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade
|
||||||
|
*/
|
||||||
|
protected $logoimage = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\Category>
|
||||||
|
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
|
||||||
|
*/
|
||||||
|
protected $categories;
|
||||||
|
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
/* Getter und Setter */
|
||||||
|
/* ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $title
|
||||||
|
*/
|
||||||
|
public function setTitle(string $title): void
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSlug(): string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $slug
|
||||||
|
*/
|
||||||
|
public function setSlug(string $slug): void
|
||||||
|
{
|
||||||
|
$this->slug = $slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSinglepid(): string
|
||||||
|
{
|
||||||
|
return $this->singlepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $singlepid
|
||||||
|
*/
|
||||||
|
public function setSinglepid(string $singlepid): void
|
||||||
|
{
|
||||||
|
$this->singlepid = $singlepid;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the teaser
|
||||||
|
*
|
||||||
|
* @return string $kteasereywords
|
||||||
|
*/
|
||||||
|
public function getTeaser()
|
||||||
|
{
|
||||||
|
return $this->teaser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the teaser
|
||||||
|
*
|
||||||
|
* @param string $teaser
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTeaser($teaser)
|
||||||
|
{
|
||||||
|
$this->teaser = $teaser;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the subtitle
|
||||||
|
*
|
||||||
|
* @return string $subtitle
|
||||||
|
*/
|
||||||
|
public function getSubtitle()
|
||||||
|
{
|
||||||
|
return $this->subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subtitle
|
||||||
|
*
|
||||||
|
* @param string $subtitle
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSubtitle($subtitle)
|
||||||
|
{
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideonapp
|
||||||
|
*
|
||||||
|
* @return bool $hideonapp
|
||||||
|
*/
|
||||||
|
public function getHideonapp()
|
||||||
|
{
|
||||||
|
return $this->hideonapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideonapp
|
||||||
|
*
|
||||||
|
* @param bool $hideonapp
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideonapp($hideonapp)
|
||||||
|
{
|
||||||
|
$this->hideonapp = $hideonapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hideonwebsite
|
||||||
|
*
|
||||||
|
* @return bool $hideonwebsite
|
||||||
|
*/
|
||||||
|
public function getHideonwebsite()
|
||||||
|
{
|
||||||
|
return $this->hideonwebsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hideonwebsite
|
||||||
|
*
|
||||||
|
* @param bool $hideonwebsite
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setHideonwebsite($hideonwebsite)
|
||||||
|
{
|
||||||
|
$this->hideonwebsite = $hideonwebsite;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the description
|
||||||
|
*
|
||||||
|
* @return string $description
|
||||||
|
*/
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the description
|
||||||
|
*
|
||||||
|
* @param string $description
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setDescription($description)
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the caseimage
|
||||||
|
*
|
||||||
|
* @return \TYPO3\CMS\Extbase\Domain\Model\FileReference $caseimage
|
||||||
|
*/
|
||||||
|
public function getCaseimage()
|
||||||
|
{
|
||||||
|
return $this->caseimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the caseimage
|
||||||
|
*
|
||||||
|
* @param \TYPO3\CMS\Extbase\Domain\Model\FileReference $caseimage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCaseimage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $caseimage)
|
||||||
|
{
|
||||||
|
$this->caseimage = $caseimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the logoimage
|
||||||
|
*
|
||||||
|
* @return \TYPO3\CMS\Extbase\Domain\Model\FileReference $logoimage
|
||||||
|
*/
|
||||||
|
public function getLogoimage()
|
||||||
|
{
|
||||||
|
return $this->logoimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the logoimage
|
||||||
|
*
|
||||||
|
* @param \TYPO3\CMS\Extbase\Domain\Model\FileReference $logoimage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setLogoimage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $logoimage)
|
||||||
|
{
|
||||||
|
$this->logoimage = $logoimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the ObjectStorage for categories
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->categories = new ObjectStorage();
|
||||||
|
$this->productimage = new ObjectStorage();
|
||||||
|
$this->downloads = new ObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a category
|
||||||
|
*
|
||||||
|
* @param Category $category
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addCategory(Category $category)
|
||||||
|
{
|
||||||
|
$this->categories->attach($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a category
|
||||||
|
*
|
||||||
|
* @param Category $categoryToRemove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeCategory(Category $categoryToRemove)
|
||||||
|
{
|
||||||
|
$this->categories->detach($categoryToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the categories
|
||||||
|
*
|
||||||
|
* @return ObjectStorage<Category>
|
||||||
|
*/
|
||||||
|
public function getCategories()
|
||||||
|
{
|
||||||
|
return $this->categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the categories
|
||||||
|
*
|
||||||
|
* @param ObjectStorage<Category> $categories
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCategories(ObjectStorage $categories)
|
||||||
|
{
|
||||||
|
$this->categories = $categories;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository for Downloads
|
||||||
|
*/
|
||||||
|
class DownloadRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
|
||||||
|
{
|
||||||
|
public function findByUids(array $uids)
|
||||||
|
{
|
||||||
|
$query = $this->createQuery();
|
||||||
|
return $query->matching(
|
||||||
|
$query->in('uid', $uids) // Use 'uid' instead of 'uids'
|
||||||
|
)->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Repository;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||||
|
|
||||||
|
class MarketRepository extends Repository
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
namespace Evomedien\Vitec\Domain\Repository;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||||
|
|
||||||
|
class ProductRepository extends Repository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Find products with visibility filters and optional category filtering
|
||||||
|
*
|
||||||
|
* @param array $categoryUids Optional category UIDs to filter by
|
||||||
|
* @param bool $respectVisibility Whether to apply visibility filters
|
||||||
|
* @return QueryResultInterface
|
||||||
|
*/
|
||||||
|
public function findFilteredProducts(array $categoryUids = [], bool $respectVisibility = true): QueryResultInterface
|
||||||
|
{
|
||||||
|
$query = $this->createQuery();
|
||||||
|
$constraints = [];
|
||||||
|
|
||||||
|
// Add visibility constraints if requested
|
||||||
|
if ($respectVisibility) {
|
||||||
|
$constraints[] = $query->equals('legacy', 0);
|
||||||
|
$constraints[] = $query->equals('supportproduct', 0);
|
||||||
|
$constraints[] = $query->equals('hideonwebsite', 0);
|
||||||
|
$constraints[] = $query->equals('hideonproducts', 0);
|
||||||
|
$constraints[] = $query->equals('subproduct', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add category filtering if categories are provided
|
||||||
|
$categoryUids = array_filter(array_map('intval', $categoryUids));
|
||||||
|
if (!empty($categoryUids)) {
|
||||||
|
// For TYPO3 13.4, use contains() for MM relations to sys_category
|
||||||
|
$categoryConstraints = [];
|
||||||
|
foreach ($categoryUids as $categoryUid) {
|
||||||
|
$categoryConstraints[] = $query->contains('categories', $categoryUid);
|
||||||
|
}
|
||||||
|
$constraints[] = $query->logicalOr(...$categoryConstraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply constraints if any exist
|
||||||
|
if (!empty($constraints)) {
|
||||||
|
$query->matching($query->logicalAnd(...$constraints));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all visible products (convenience method)
|
||||||
|
*/
|
||||||
|
public function findAllVisible(): QueryResultInterface
|
||||||
|
{
|
||||||
|
return $this->findFilteredProducts([], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find products by categories only (no visibility filtering)
|
||||||
|
*/
|
||||||
|
public function findByCategoriesOnly(array $categoryUids): QueryResultInterface
|
||||||
|
{
|
||||||
|
return $this->findFilteredProducts($categoryUids, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Domain\Repository;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||||
|
|
||||||
|
class SolutionRepository extends Repository
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
namespace Evomedien\Vitec\Domain\Repository;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the "VITEC" Extension for TYPO3 CMS.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* (c) 2025
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository for Usecase
|
||||||
|
*/
|
||||||
|
class UsecaseRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
|
||||||
|
{
|
||||||
|
|
||||||
|
public function findAllWithCategories(): QueryResultInterface
|
||||||
|
{
|
||||||
|
$query = $this->createQuery();
|
||||||
|
$query->getQuerySettings()->setRespectStoragePage(false); // Fetch from all storage pages
|
||||||
|
return $query->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
52
packages/vitec/Classes/EventListener/ShowFrontendLayoutBanner.php
Executable file
52
packages/vitec/Classes/EventListener/ShowFrontendLayoutBanner.php
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\EventListener;
|
||||||
|
|
||||||
|
use Evomedien\Vitec\View\BackendLayoutDataProvider;
|
||||||
|
use TYPO3\CMS\Backend\Controller\Event\ModifyPageLayoutContentEvent;
|
||||||
|
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||||
|
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an info banner at the top of the page module showing the active
|
||||||
|
* VITEC frontend layout name. Helps editors immediately see which layout
|
||||||
|
* the current page is using.
|
||||||
|
*/
|
||||||
|
#[AsEventListener(identifier: 'vitec/show-frontend-layout-banner')]
|
||||||
|
final class ShowFrontendLayoutBanner
|
||||||
|
{
|
||||||
|
public function __invoke(ModifyPageLayoutContentEvent $event): void
|
||||||
|
{
|
||||||
|
$request = $event->getRequest();
|
||||||
|
$pageId = (int)($request->getQueryParams()['id'] ?? 0);
|
||||||
|
if ($pageId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = BackendUtility::getRecord('pages', $pageId, 'layout');
|
||||||
|
if (!is_array($page)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$layoutValue = (int)($page['layout'] ?? 0);
|
||||||
|
$title = BackendLayoutDataProvider::LAYOUT_MAP[$layoutValue]['title'] ?? null;
|
||||||
|
if ($title === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$banner = sprintf(
|
||||||
|
'<div class="callout callout-info" style="margin-bottom:1rem">'
|
||||||
|
. '<div class="callout-content">'
|
||||||
|
. '<div class="callout-title">Frontend Layout</div>'
|
||||||
|
. '<div class="callout-body"><strong>%s</strong> '
|
||||||
|
. '<span class="text-body-secondary">(value: %d)</span></div>'
|
||||||
|
. '</div>'
|
||||||
|
. '</div>',
|
||||||
|
htmlspecialchars($title, ENT_QUOTES, 'UTF-8'),
|
||||||
|
$layoutValue
|
||||||
|
);
|
||||||
|
|
||||||
|
$event->addHeaderContent($banner);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
packages/vitec/Classes/Hook/SyncBackendLayoutHook.php
Executable file
37
packages/vitec/Classes/Hook/SyncBackendLayoutHook.php
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Hook;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-syncs pages.backend_layout = "vitec_<N>" whenever pages.layout is saved.
|
||||||
|
* Also syncs backend_layout_next_level so subpages inherit the same layout
|
||||||
|
* (until they pick their own frontend layout).
|
||||||
|
*
|
||||||
|
* Hooked via $TYPO3_CONF_VARS[SC_OPTIONS][t3lib/class.t3lib_tcemain.php][processDatamapClass].
|
||||||
|
*/
|
||||||
|
final class SyncBackendLayoutHook
|
||||||
|
{
|
||||||
|
public function processDatamap_postProcessFieldArray(
|
||||||
|
string $status,
|
||||||
|
string $table,
|
||||||
|
$id,
|
||||||
|
array &$fieldArray,
|
||||||
|
DataHandler $pObj
|
||||||
|
): void {
|
||||||
|
if ($table !== 'pages') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!array_key_exists('layout', $fieldArray)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$layoutValue = (int)$fieldArray['layout'];
|
||||||
|
$beIdentifier = 'vitec__' . $layoutValue;
|
||||||
|
|
||||||
|
$fieldArray['backend_layout'] = $beIdentifier;
|
||||||
|
$fieldArray['backend_layout_next_level'] = $beIdentifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\PageTitle;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\PageTitle\AbstractPageTitleProvider;
|
||||||
|
|
||||||
|
final class ProductPageTitleProvider extends AbstractPageTitleProvider
|
||||||
|
{
|
||||||
|
private string $seotitle = '';
|
||||||
|
|
||||||
|
public function setSeoTitle(string $seotitle): void
|
||||||
|
{
|
||||||
|
$this->seotitle = $seotitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->seotitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
packages/vitec/Classes/Preview/UsecaseshowPluginPreview.php
Normal file
86
packages/vitec/Classes/Preview/UsecaseshowPluginPreview.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\Preview;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer;
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\UsecaseRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preview class for the Usecaseshow plugin
|
||||||
|
*/
|
||||||
|
class UsecaseshowPluginPreview extends StandardContentPreviewRenderer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param GridColumnItem $item
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function renderPageModulePreviewContent(GridColumnItem $item): string
|
||||||
|
{
|
||||||
|
$record = $item->getRecord();
|
||||||
|
|
||||||
|
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||||
|
$view->setTemplatePathAndFilename('EXT:vitec/Resources/Private/Templates/Preview/Usecaseshow.html');
|
||||||
|
|
||||||
|
// Parse FlexForm data
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($record['pi_flexform'] ?? '');
|
||||||
|
|
||||||
|
// Get usecase data if selected
|
||||||
|
$usecaseData = null;
|
||||||
|
$usecaseUid = (int)($flexFormData['settings']['usecase'] ?? 0);
|
||||||
|
|
||||||
|
if ($usecaseUid > 0) {
|
||||||
|
$usecaseRepository = GeneralUtility::makeInstance(UsecaseRepository::class);
|
||||||
|
$usecase = $usecaseRepository->findByUid($usecaseUid);
|
||||||
|
if ($usecase) {
|
||||||
|
$usecaseData = [
|
||||||
|
'uid' => $usecase->getUid(),
|
||||||
|
'title' => $usecase->getTitle(),
|
||||||
|
'subtitle' => $usecase->getSubtitle(),
|
||||||
|
'description' => $usecase->getDescription()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get layout setting
|
||||||
|
$layout = $flexFormData['settings']['layout'] ?? 'default';
|
||||||
|
|
||||||
|
// Assign variables to view
|
||||||
|
$view->assignMultiple([
|
||||||
|
'record' => $record,
|
||||||
|
'flexFormData' => $flexFormData,
|
||||||
|
'usecase' => $usecaseData,
|
||||||
|
'layout' => $layout,
|
||||||
|
'pluginName' => 'Usecaseshow'
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $view->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GridColumnItem $item
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function renderPageModulePreviewHeader(GridColumnItem $item): string
|
||||||
|
{
|
||||||
|
$record = $item->getRecord();
|
||||||
|
|
||||||
|
// Parse FlexForm to get usecase info for header
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($record['pi_flexform'] ?? '');
|
||||||
|
|
||||||
|
$usecaseUid = (int)($flexFormData['settings']['usecase'] ?? 0);
|
||||||
|
|
||||||
|
if ($usecaseUid > 0) {
|
||||||
|
return '<strong>🎯 Usecase Show Plugin</strong> <small>(Usecase ID: ' . $usecaseUid . ')</small>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<strong>🎯 Usecase Show Plugin</strong> <small style="color: orange;">(No usecase selected)</small>';
|
||||||
|
}
|
||||||
|
}
|
||||||
499
packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php
Normal file
499
packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\UserFunc;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use TYPO3\CMS\Core\Database\Connection;
|
||||||
|
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||||
|
use TYPO3\CMS\Core\Resource\FileReference;
|
||||||
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Extbase\Service\ImageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserFunc to render product list as JSON for headless
|
||||||
|
*/
|
||||||
|
class ProductListJsonRenderer
|
||||||
|
{
|
||||||
|
public function render(string $content, array $conf): string
|
||||||
|
{
|
||||||
|
// IMPORTANT: Headless creates new cObj contexts when rendering JSON fields,
|
||||||
|
// losing the current content element context. Also, Extbase repositories don't work
|
||||||
|
// in UserFunc context because the full Extbase framework isn't bootstrapped.
|
||||||
|
//
|
||||||
|
// Solution: Query tt_content directly to find the plugin configuration,
|
||||||
|
// then use direct database queries for products instead of Extbase repositories.
|
||||||
|
|
||||||
|
// Get current page UID
|
||||||
|
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
|
||||||
|
|
||||||
|
// Query tt_content for vitec_productlist on this page
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
|
||||||
|
$contentElements = $queryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if (empty($contentElements)) {
|
||||||
|
return json_encode(['debug' => 'No vitec_productlist on page ' . $pageId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the first one (there should typically be only one)
|
||||||
|
$contentElement = $contentElements[0];
|
||||||
|
|
||||||
|
// Parse FlexForm
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Extract category UIDs and debug setting
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
$debugMode = (bool)($settings['debug'] ?? false);
|
||||||
|
$allProducts = (bool)($settings['allproducts'] ?? false);
|
||||||
|
|
||||||
|
// Extbase repositories don't work in UserFunc context
|
||||||
|
// Use direct database query instead
|
||||||
|
$productQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_product');
|
||||||
|
|
||||||
|
$productQuery = $productQueryBuilder
|
||||||
|
->select('p.*')
|
||||||
|
->from('tx_vitec_domain_model_product', 'p')
|
||||||
|
->where(
|
||||||
|
$productQueryBuilder->expr()->eq('p.deleted', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hidden', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.legacy', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.supportproduct', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hideonwebsite', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hideonproducts', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.subproduct', 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add category filter if specified and allProducts is not enabled
|
||||||
|
if (!empty($categoryUids) && !$allProducts) {
|
||||||
|
// Join with sys_category_record_mm to filter by categories
|
||||||
|
$productQuery
|
||||||
|
->join(
|
||||||
|
'p',
|
||||||
|
'sys_category_record_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = p.uid AND mm.tablenames = ' . $productQueryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . ' AND mm.fieldname = ' . $productQueryBuilder->createNamedParameter('categories', ParameterType::STRING)
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$productQueryBuilder->expr()->in('mm.uid_local', $productQueryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY))
|
||||||
|
)
|
||||||
|
->groupBy('p.uid');
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = $productQuery->executeQuery()->fetchAllAssociative();
|
||||||
|
|
||||||
|
// Serialize products (they're already associative arrays from the query)
|
||||||
|
$productsData = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$productsData[] = $this->serializeProduct($product);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If debug mode is enabled, return object with products and debug info
|
||||||
|
if ($debugMode) {
|
||||||
|
return json_encode([
|
||||||
|
'products' => $productsData,
|
||||||
|
'debug' => [
|
||||||
|
'pageId' => $pageId,
|
||||||
|
'categoryUids' => $categoryUids,
|
||||||
|
'productCount' => count($productsData),
|
||||||
|
'settings' => $settings
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise return products array directly (backward compatible)
|
||||||
|
return json_encode($productsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a single product DB row to the full headless JSON structure.
|
||||||
|
*
|
||||||
|
* Includes every field declared on the Product domain model
|
||||||
|
* (Evomedien\Vitec\Domain\Model\Product) plus all fully resolved
|
||||||
|
* relations (categories, productimage, downloads, ogimage, relatedprodukt).
|
||||||
|
*
|
||||||
|
* DB-only columns that are NOT part of the domain model
|
||||||
|
* (cta, links, sorting1-5, key1-3, apptext1-3, productlayout, image,
|
||||||
|
* relatedimage, system fields) are intentionally omitted.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $product Associative DB row of tx_vitec_domain_model_product
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
protected function serializeProduct(array $product): array
|
||||||
|
{
|
||||||
|
$uid = (int)$product['uid'];
|
||||||
|
|
||||||
|
return [
|
||||||
|
// --- identifier ---
|
||||||
|
'uid' => $uid,
|
||||||
|
|
||||||
|
// --- scalar string fields (Product domain model) ---
|
||||||
|
'title' => (string)($product['title'] ?? ''),
|
||||||
|
'slug' => (string)($product['slug'] ?? ''),
|
||||||
|
'urltitle' => (string)($product['urltitle'] ?? ''),
|
||||||
|
'seotitle' => (string)($product['seotitle'] ?? ''),
|
||||||
|
'seometa' => (string)($product['seometa'] ?? ''),
|
||||||
|
'keywords' => (string)($product['keywords'] ?? ''),
|
||||||
|
'structureddata' => (string)($product['structureddata'] ?? ''),
|
||||||
|
'teaser' => (string)($product['teaser'] ?? ''),
|
||||||
|
'subtitle' => (string)($product['subtitle'] ?? ''),
|
||||||
|
'video' => (string)($product['video'] ?? ''),
|
||||||
|
'applications' => (string)($product['applications'] ?? ''),
|
||||||
|
'description' => (string)($product['description'] ?? ''),
|
||||||
|
'highlights' => (string)($product['highlights'] ?? ''),
|
||||||
|
'shortcutpid' => (string)($product['shortcutpid'] ?? ''),
|
||||||
|
'contentelement' => (string)($product['contentelement'] ?? ''),
|
||||||
|
'contentelementcta' => (string)($product['contentelementcta'] ?? ''),
|
||||||
|
|
||||||
|
// --- boolean flags (Product domain model) ---
|
||||||
|
'hideonapp' => (bool)($product['hideonapp'] ?? false),
|
||||||
|
'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false),
|
||||||
|
'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false),
|
||||||
|
'hideonproducts' => (bool)($product['hideonproducts'] ?? false),
|
||||||
|
'shortcut' => (bool)($product['shortcut'] ?? false),
|
||||||
|
'legacy' => (bool)($product['legacy'] ?? false),
|
||||||
|
'supportproduct' => (bool)($product['supportproduct'] ?? false),
|
||||||
|
'subproduct' => (bool)($product['subproduct'] ?? false),
|
||||||
|
|
||||||
|
// --- convenience link (kept for backward compatibility) ---
|
||||||
|
'link' => '/product/' . (string)($product['slug'] ?? ''),
|
||||||
|
|
||||||
|
// --- fully resolved relations ---
|
||||||
|
'categories' => $this->getProductCategories($uid),
|
||||||
|
'images' => $this->getProductImages($uid),
|
||||||
|
'downloads' => $this->getProductDownloads($uid),
|
||||||
|
'ogimage' => $this->getProductOgImage($uid),
|
||||||
|
'relatedprodukt' => $this->getRelatedProducts($uid),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get product images from FAL (sys_file_reference)
|
||||||
|
* Processes images through ImageService and generates srcset for responsive images
|
||||||
|
*/
|
||||||
|
protected function getProductImages(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileReferences = $queryBuilder
|
||||||
|
->select('sfr.uid', 'sfr.uid_local', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop')
|
||||||
|
->from('sys_file_reference', 'sfr')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('sfr.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('sfr.sorting_foreign')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
||||||
|
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
||||||
|
|
||||||
|
$images = [];
|
||||||
|
foreach ($fileReferences as $fileRefData) {
|
||||||
|
try {
|
||||||
|
// Get FAL FileReference object
|
||||||
|
$fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']);
|
||||||
|
|
||||||
|
// Define image sizes for srcset
|
||||||
|
$sizes = [
|
||||||
|
'small' => ['width' => 400, 'height' => null],
|
||||||
|
'medium' => ['width' => 800, 'height' => null],
|
||||||
|
'large' => ['width' => 1200, 'height' => null],
|
||||||
|
'xlarge' => ['width' => 1600, 'height' => null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$srcset = [];
|
||||||
|
foreach ($sizes as $sizeName => $dimensions) {
|
||||||
|
$processedImage = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
[
|
||||||
|
'width' => $dimensions['width'],
|
||||||
|
'height' => $dimensions['height'],
|
||||||
|
'crop' => $fileRefData['crop'] ?? null
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$imageUri = $imageService->getImageUri($processedImage);
|
||||||
|
$srcset[] = [
|
||||||
|
'url' => $imageUri,
|
||||||
|
'width' => $dimensions['width'],
|
||||||
|
'descriptor' => $dimensions['width'] . 'w'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get original/default image
|
||||||
|
$defaultProcessed = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
['width' => 800, 'crop' => $fileRefData['crop'] ?? null]
|
||||||
|
);
|
||||||
|
|
||||||
|
$images[] = [
|
||||||
|
'uid' => (int)$fileRefData['uid'],
|
||||||
|
'url' => $imageService->getImageUri($defaultProcessed),
|
||||||
|
'title' => $fileRefData['title'] ?? '',
|
||||||
|
'alternative' => $fileRefData['alternative'] ?? '',
|
||||||
|
'description' => $fileRefData['description'] ?? '',
|
||||||
|
'srcset' => $srcset,
|
||||||
|
'properties' => [
|
||||||
|
'width' => $fileReference->getProperty('width'),
|
||||||
|
'height' => $fileReference->getProperty('height'),
|
||||||
|
'mimeType' => $fileReference->getProperty('mime_type')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Skip images that can't be processed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the single Open Graph image (ogimage) for a product, or null.
|
||||||
|
*
|
||||||
|
* @return array<string,mixed>|null
|
||||||
|
*/
|
||||||
|
protected function getProductOgImage(int $productUid): ?array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileRefData = $queryBuilder
|
||||||
|
->select('sfr.uid', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop')
|
||||||
|
->from('sys_file_reference', 'sfr')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('sfr.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('sfr.sorting_foreign')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAssociative();
|
||||||
|
|
||||||
|
if (!$fileRefData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
||||||
|
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
||||||
|
$fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']);
|
||||||
|
|
||||||
|
$processed = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
['width' => 1200, 'crop' => $fileRefData['crop'] ?? null]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'uid' => (int)$fileRefData['uid'],
|
||||||
|
'url' => $imageService->getImageUri($processed),
|
||||||
|
'title' => $fileRefData['title'] ?? '',
|
||||||
|
'alternative' => $fileRefData['alternative'] ?? '',
|
||||||
|
'description' => $fileRefData['description'] ?? '',
|
||||||
|
'properties' => [
|
||||||
|
'width' => $fileReference->getProperty('width'),
|
||||||
|
'height' => $fileReference->getProperty('height'),
|
||||||
|
'mimeType' => $fileReference->getProperty('mime_type'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get categories for a product (resolved sys_category records).
|
||||||
|
*/
|
||||||
|
protected function getProductCategories(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_category');
|
||||||
|
|
||||||
|
$categories = $queryBuilder
|
||||||
|
->select('c.uid', 'c.title', 'c.description')
|
||||||
|
->from('sys_category', 'c')
|
||||||
|
->join(
|
||||||
|
'c',
|
||||||
|
'sys_category_record_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_local = c.uid AND mm.tablenames = ' .
|
||||||
|
$queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) .
|
||||||
|
' AND mm.fieldname = ' .
|
||||||
|
$queryBuilder->createNamedParameter('categories', ParameterType::STRING)
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('c.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('c.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('mm.sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
return array_map(static function ($cat) {
|
||||||
|
return [
|
||||||
|
'uid' => (int)$cat['uid'],
|
||||||
|
'title' => $cat['title'] ?? '',
|
||||||
|
'description' => $cat['description'] ?? '',
|
||||||
|
];
|
||||||
|
}, $categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all downloads for a product (resolved via tx_vitec_product_download_mm).
|
||||||
|
*/
|
||||||
|
protected function getProductDownloads(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_download');
|
||||||
|
|
||||||
|
$downloads = $queryBuilder
|
||||||
|
->select('d.*')
|
||||||
|
->from('tx_vitec_domain_model_download', 'd')
|
||||||
|
->join(
|
||||||
|
'd',
|
||||||
|
'tx_vitec_product_download_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = d.uid'
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('d.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('d.hidden', 0),
|
||||||
|
$queryBuilder->expr()->eq('d.hideonwebsite', 0)
|
||||||
|
)
|
||||||
|
->orderBy('mm.sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($downloads as $download) {
|
||||||
|
$fileInfo = null;
|
||||||
|
|
||||||
|
// Get file information from FAL if file reference exists
|
||||||
|
if (!empty($download['file'])) {
|
||||||
|
$fileQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileRef = $fileQueryBuilder
|
||||||
|
->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type')
|
||||||
|
->from('sys_file_reference', 'fr')
|
||||||
|
->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid')
|
||||||
|
->where(
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.deleted', 0),
|
||||||
|
$fileQueryBuilder->expr()->eq('f.missing', 0)
|
||||||
|
)
|
||||||
|
->orderBy('fr.sorting_foreign', 'ASC')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAssociative();
|
||||||
|
|
||||||
|
if ($fileRef) {
|
||||||
|
$fileInfo = [
|
||||||
|
'uid' => (int)$fileRef['file_uid'],
|
||||||
|
'name' => $fileRef['name'],
|
||||||
|
'url' => '/fileadmin' . $fileRef['identifier'],
|
||||||
|
'size' => (int)$fileRef['size'],
|
||||||
|
'extension' => $fileRef['extension'],
|
||||||
|
'mimeType' => $fileRef['mime_type'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
'uid' => (int)$download['uid'],
|
||||||
|
'title' => $download['title'] ?? '',
|
||||||
|
'slug' => $download['slug'] ?? '',
|
||||||
|
'teaser' => $download['teaser'] ?? '',
|
||||||
|
'description' => $download['description'] ?? '',
|
||||||
|
'keywords' => $download['keywords'] ?? '',
|
||||||
|
'icon' => $download['icon'] ?? '',
|
||||||
|
'file' => $fileInfo,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get related products (resolved via tx_vitec_product_related_mm).
|
||||||
|
*
|
||||||
|
* Returns a shallow representation (no nested relations) to avoid
|
||||||
|
* infinite recursion between mutually related products.
|
||||||
|
*/
|
||||||
|
protected function getRelatedProducts(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_product');
|
||||||
|
|
||||||
|
$related = $queryBuilder
|
||||||
|
->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description')
|
||||||
|
->from('tx_vitec_domain_model_product', 'p')
|
||||||
|
->join(
|
||||||
|
'p',
|
||||||
|
'tx_vitec_product_related_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = p.uid'
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('p.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('p.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('mm.sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($related as $rel) {
|
||||||
|
$relUid = (int)$rel['uid'];
|
||||||
|
$images = $this->getProductImages($relUid);
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
'uid' => $relUid,
|
||||||
|
'title' => (string)($rel['title'] ?? ''),
|
||||||
|
'slug' => (string)($rel['slug'] ?? ''),
|
||||||
|
'subtitle' => (string)($rel['subtitle'] ?? ''),
|
||||||
|
'teaser' => (string)($rel['teaser'] ?? ''),
|
||||||
|
'description' => (string)($rel['description'] ?? ''),
|
||||||
|
'link' => '/product/' . (string)($rel['slug'] ?? ''),
|
||||||
|
'images' => $images,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
228
packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260518144047
Executable file
228
packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260518144047
Executable file
@@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\UserFunc;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use TYPO3\CMS\Core\Database\Connection;
|
||||||
|
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||||
|
use TYPO3\CMS\Core\Resource\FileReference;
|
||||||
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Extbase\Service\ImageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserFunc to render product list as JSON for headless
|
||||||
|
*/
|
||||||
|
class ProductListJsonRenderer
|
||||||
|
{
|
||||||
|
public function render(string $content, array $conf): string
|
||||||
|
{
|
||||||
|
// IMPORTANT: Headless creates new cObj contexts when rendering JSON fields,
|
||||||
|
// losing the current content element context. Also, Extbase repositories don't work
|
||||||
|
// in UserFunc context because the full Extbase framework isn't bootstrapped.
|
||||||
|
//
|
||||||
|
// Solution: Query tt_content directly to find the plugin configuration,
|
||||||
|
// then use direct database queries for products instead of Extbase repositories.
|
||||||
|
|
||||||
|
// Get current page UID
|
||||||
|
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
|
||||||
|
|
||||||
|
// Query tt_content for vitec_productlist on this page
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
|
||||||
|
$contentElements = $queryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if (empty($contentElements)) {
|
||||||
|
return json_encode(['debug' => 'No vitec_productlist on page ' . $pageId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the first one (there should typically be only one)
|
||||||
|
$contentElement = $contentElements[0];
|
||||||
|
|
||||||
|
// Parse FlexForm
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Extract category UIDs and debug setting
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
$debugMode = (bool)($settings['debug'] ?? false);
|
||||||
|
$allProducts = (bool)($settings['allproducts'] ?? false);
|
||||||
|
|
||||||
|
// Extbase repositories don't work in UserFunc context
|
||||||
|
// Use direct database query instead
|
||||||
|
$productQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_product');
|
||||||
|
|
||||||
|
$productQuery = $productQueryBuilder
|
||||||
|
->select('p.*')
|
||||||
|
->from('tx_vitec_domain_model_product', 'p')
|
||||||
|
->where(
|
||||||
|
$productQueryBuilder->expr()->eq('p.deleted', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hidden', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.legacy', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.supportproduct', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hideonwebsite', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.hideonproducts', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('p.subproduct', 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add category filter if specified and allProducts is not enabled
|
||||||
|
if (!empty($categoryUids) && !$allProducts) {
|
||||||
|
// Join with sys_category_record_mm to filter by categories
|
||||||
|
$productQuery
|
||||||
|
->join(
|
||||||
|
'p',
|
||||||
|
'sys_category_record_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = p.uid AND mm.tablenames = ' . $productQueryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . ' AND mm.fieldname = ' . $productQueryBuilder->createNamedParameter('categories', ParameterType::STRING)
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$productQueryBuilder->expr()->in('mm.uid_local', $productQueryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY))
|
||||||
|
)
|
||||||
|
->groupBy('p.uid');
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = $productQuery->executeQuery()->fetchAllAssociative();
|
||||||
|
|
||||||
|
// Serialize products (they're already associative arrays from the query)
|
||||||
|
$productsData = [];
|
||||||
|
foreach ($products as $product) {
|
||||||
|
// Get product images (FAL references)
|
||||||
|
$images = $this->getProductImages((int)$product['uid']);
|
||||||
|
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => (int)$product['uid'],
|
||||||
|
'title' => $product['title'] ?? '',
|
||||||
|
'subtitle' => $product['subtitle'] ?? '',
|
||||||
|
'slug' => $product['slug'] ?? '',
|
||||||
|
'teaser' => $product['teaser'] ?? '',
|
||||||
|
'description' => $product['description'] ?? '',
|
||||||
|
'link' => '/product/' . ($product['slug'] ?? ''),
|
||||||
|
'images' => $images,
|
||||||
|
// Add more fields as needed
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If debug mode is enabled, return object with products and debug info
|
||||||
|
if ($debugMode) {
|
||||||
|
return json_encode([
|
||||||
|
'products' => $productsData,
|
||||||
|
'debug' => [
|
||||||
|
'pageId' => $pageId,
|
||||||
|
'categoryUids' => $categoryUids,
|
||||||
|
'productCount' => count($productsData),
|
||||||
|
'settings' => $settings
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise return products array directly (backward compatible)
|
||||||
|
return json_encode($productsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get product images from FAL (sys_file_reference)
|
||||||
|
* Processes images through ImageService and generates srcset for responsive images
|
||||||
|
*/
|
||||||
|
protected function getProductImages(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileReferences = $queryBuilder
|
||||||
|
->select('sfr.uid', 'sfr.uid_local', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop')
|
||||||
|
->from('sys_file_reference', 'sfr')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('sfr.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('sfr.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('sfr.sorting_foreign')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
||||||
|
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
||||||
|
|
||||||
|
$images = [];
|
||||||
|
foreach ($fileReferences as $fileRefData) {
|
||||||
|
try {
|
||||||
|
// Get FAL FileReference object
|
||||||
|
$fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']);
|
||||||
|
|
||||||
|
// Define image sizes for srcset
|
||||||
|
$sizes = [
|
||||||
|
'small' => ['width' => 400, 'height' => null],
|
||||||
|
'medium' => ['width' => 800, 'height' => null],
|
||||||
|
'large' => ['width' => 1200, 'height' => null],
|
||||||
|
'xlarge' => ['width' => 1600, 'height' => null],
|
||||||
|
];
|
||||||
|
|
||||||
|
$srcset = [];
|
||||||
|
foreach ($sizes as $sizeName => $dimensions) {
|
||||||
|
$processedImage = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
[
|
||||||
|
'width' => $dimensions['width'],
|
||||||
|
'height' => $dimensions['height'],
|
||||||
|
'crop' => $fileRefData['crop'] ?? null
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$imageUri = $imageService->getImageUri($processedImage);
|
||||||
|
$srcset[] = [
|
||||||
|
'url' => $imageUri,
|
||||||
|
'width' => $dimensions['width'],
|
||||||
|
'descriptor' => $dimensions['width'] . 'w'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get original/default image
|
||||||
|
$defaultProcessed = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
['width' => 800, 'crop' => $fileRefData['crop'] ?? null]
|
||||||
|
);
|
||||||
|
|
||||||
|
$images[] = [
|
||||||
|
'uid' => (int)$fileRefData['uid'],
|
||||||
|
'url' => $imageService->getImageUri($defaultProcessed),
|
||||||
|
'title' => $fileRefData['title'] ?? '',
|
||||||
|
'alternative' => $fileRefData['alternative'] ?? '',
|
||||||
|
'description' => $fileRefData['description'] ?? '',
|
||||||
|
'srcset' => $srcset,
|
||||||
|
'properties' => [
|
||||||
|
'width' => $fileReference->getProperty('width'),
|
||||||
|
'height' => $fileReference->getProperty('height'),
|
||||||
|
'mimeType' => $fileReference->getProperty('mime_type')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Skip images that can't be processed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $images;
|
||||||
|
}
|
||||||
|
}
|
||||||
366
packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php
Normal file
366
packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\UserFunc;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||||
|
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||||
|
use TYPO3\CMS\Core\Resource\FileReference;
|
||||||
|
use TYPO3\CMS\Core\Imaging\ImageService;
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserFunc to render single product data as JSON for headless output
|
||||||
|
*/
|
||||||
|
class ProductShowJsonRenderer
|
||||||
|
{
|
||||||
|
public function render(string $content, array $conf): string
|
||||||
|
{
|
||||||
|
$pageId = (int)$GLOBALS['TSFE']->id;
|
||||||
|
|
||||||
|
// DEBUG: Log that the UserFunc is being called
|
||||||
|
$debugInfo = [
|
||||||
|
'userFuncCalled' => true,
|
||||||
|
'pageId' => $pageId,
|
||||||
|
'requestUri' => $_SERVER['REQUEST_URI'] ?? 'unknown',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Query tt_content for vitec_productshow on this page
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
|
||||||
|
$contentElements = $queryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productshow', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$debugInfo['contentElementsFound'] = count($contentElements);
|
||||||
|
|
||||||
|
if (empty($contentElements)) {
|
||||||
|
$debugInfo['error'] = 'No vitec_productshow on page';
|
||||||
|
return json_encode(['debug' => $debugInfo]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the first one
|
||||||
|
$contentElement = $contentElements[0];
|
||||||
|
|
||||||
|
// Parse FlexForm
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Get product UID from FlexForm or route parameter
|
||||||
|
$productUid = (int)($settings['product'] ?? 0);
|
||||||
|
$layout = (int)($settings['layout'] ?? 0);
|
||||||
|
$debugMode = (bool)($settings['debug'] ?? false);
|
||||||
|
|
||||||
|
// If no product selected in FlexForm, try to get from route parameter
|
||||||
|
if (!$productUid) {
|
||||||
|
// Get the product parameter from GET request
|
||||||
|
$routeParams = $GLOBALS['TYPO3_REQUEST']->getQueryParams();
|
||||||
|
$productParam = $routeParams['tx_vitec_productshow']['product'] ?? null;
|
||||||
|
|
||||||
|
if ($productParam) {
|
||||||
|
// If it's a slug, resolve it to UID
|
||||||
|
if (!is_numeric($productParam)) {
|
||||||
|
$slugQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_product');
|
||||||
|
|
||||||
|
$productBySlug = $slugQueryBuilder
|
||||||
|
->select('uid')
|
||||||
|
->from('tx_vitec_domain_model_product')
|
||||||
|
->where(
|
||||||
|
$slugQueryBuilder->expr()->eq('slug', $slugQueryBuilder->createNamedParameter($productParam)),
|
||||||
|
$slugQueryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$slugQueryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAssociative();
|
||||||
|
|
||||||
|
$productUid = (int)($productBySlug['uid'] ?? 0);
|
||||||
|
} else {
|
||||||
|
$productUid = (int)$productParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$productUid) {
|
||||||
|
return json_encode([
|
||||||
|
'error' => 'No product selected or found',
|
||||||
|
'debug' => [
|
||||||
|
'settings' => $settings,
|
||||||
|
'routeParams' => $routeParams ?? [],
|
||||||
|
'allQueryParams' => $GLOBALS['TYPO3_REQUEST']->getQueryParams() ?? [],
|
||||||
|
'requestUri' => $GLOBALS['TYPO3_REQUEST']->getUri()->getPath() ?? ''
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query product
|
||||||
|
$productQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_product');
|
||||||
|
|
||||||
|
$product = $productQueryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tx_vitec_domain_model_product')
|
||||||
|
->where(
|
||||||
|
$productQueryBuilder->expr()->eq('uid', $productQueryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$productQueryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$productQueryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAssociative();
|
||||||
|
|
||||||
|
if (!$product) {
|
||||||
|
return json_encode([
|
||||||
|
'error' => 'Product not found',
|
||||||
|
'debug' => $debugMode ? ['productUid' => $productUid] : null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get product images
|
||||||
|
$images = $this->getProductImages((int)$product['uid']);
|
||||||
|
|
||||||
|
// Get categories
|
||||||
|
$categories = $this->getProductCategories((int)$product['uid']);
|
||||||
|
|
||||||
|
// Get downloads
|
||||||
|
$downloads = $this->getProductDownloads((int)$product['uid']);
|
||||||
|
|
||||||
|
// Build response
|
||||||
|
$response = [
|
||||||
|
'product' => [
|
||||||
|
'uid' => (int)$product['uid'],
|
||||||
|
'title' => $product['title'],
|
||||||
|
'subtitle' => $product['subtitle'],
|
||||||
|
'slug' => $product['slug'],
|
||||||
|
'teaser' => $product['teaser'],
|
||||||
|
'description' => $product['description'],
|
||||||
|
'seotitle' => $product['seotitle'],
|
||||||
|
'categories' => $categories,
|
||||||
|
'images' => $images,
|
||||||
|
'downloads' => $downloads,
|
||||||
|
],
|
||||||
|
'layout' => $layout,
|
||||||
|
'settings' => [
|
||||||
|
'layout' => $layout,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($debugMode) {
|
||||||
|
$response['debug'] = [
|
||||||
|
'pageId' => $pageId,
|
||||||
|
'productUid' => $productUid,
|
||||||
|
'layout' => $layout,
|
||||||
|
'settings' => $settings,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all images for a product with FAL and ImageService processing
|
||||||
|
*/
|
||||||
|
protected function getProductImages(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileReferences = $queryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('sys_file_reference')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('sorting_foreign', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if (empty($fileReferences)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
||||||
|
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
||||||
|
$images = [];
|
||||||
|
|
||||||
|
foreach ($fileReferences as $fileRef) {
|
||||||
|
try {
|
||||||
|
$fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']);
|
||||||
|
$originalFile = $fileReference->getOriginalFile();
|
||||||
|
|
||||||
|
// Process main image
|
||||||
|
$processedImage = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
['width' => '1874c', 'height' => '625c']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate srcset
|
||||||
|
$srcset = [];
|
||||||
|
foreach ([400, 800, 1200, 1600] as $width) {
|
||||||
|
$processedVariant = $imageService->applyProcessingInstructions(
|
||||||
|
$fileReference,
|
||||||
|
['width' => $width . 'c', 'height' => (int)($width / 3) . 'c']
|
||||||
|
);
|
||||||
|
$srcset[] = [
|
||||||
|
'url' => $imageService->getImageUri($processedVariant),
|
||||||
|
'width' => $width,
|
||||||
|
'descriptor' => $width . 'w',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$images[] = [
|
||||||
|
'uid' => $fileRef['uid'],
|
||||||
|
'url' => $imageService->getImageUri($processedImage),
|
||||||
|
'title' => $fileReference->getTitle() ?: '',
|
||||||
|
'alternative' => $fileReference->getAlternative() ?: '',
|
||||||
|
'description' => $fileReference->getDescription() ?: '',
|
||||||
|
'srcset' => $srcset,
|
||||||
|
'properties' => [
|
||||||
|
'width' => $originalFile->getProperty('width'),
|
||||||
|
'height' => $originalFile->getProperty('height'),
|
||||||
|
'mimeType' => $originalFile->getMimeType(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Skip invalid file references
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get categories for a product
|
||||||
|
*/
|
||||||
|
protected function getProductCategories(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_category');
|
||||||
|
|
||||||
|
$categories = $queryBuilder
|
||||||
|
->select('c.uid', 'c.title', 'c.description')
|
||||||
|
->from('sys_category', 'c')
|
||||||
|
->join(
|
||||||
|
'c',
|
||||||
|
'sys_category_record_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_local = c.uid AND mm.tablenames = ' .
|
||||||
|
$queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) .
|
||||||
|
' AND mm.fieldname = ' .
|
||||||
|
$queryBuilder->createNamedParameter('categories', ParameterType::STRING)
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('c.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('c.hidden', 0)
|
||||||
|
)
|
||||||
|
->orderBy('mm.sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
return array_map(function ($cat) {
|
||||||
|
return [
|
||||||
|
'uid' => (int)$cat['uid'],
|
||||||
|
'title' => $cat['title'],
|
||||||
|
'description' => $cat['description'] ?? '',
|
||||||
|
];
|
||||||
|
}, $categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all downloads for a product
|
||||||
|
*/
|
||||||
|
protected function getProductDownloads(int $productUid): array
|
||||||
|
{
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_vitec_domain_model_download');
|
||||||
|
|
||||||
|
$downloads = $queryBuilder
|
||||||
|
->select('d.*')
|
||||||
|
->from('tx_vitec_domain_model_download', 'd')
|
||||||
|
->join(
|
||||||
|
'd',
|
||||||
|
'tx_vitec_product_download_mm',
|
||||||
|
'mm',
|
||||||
|
'mm.uid_foreign = d.uid'
|
||||||
|
)
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('d.deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('d.hidden', 0),
|
||||||
|
$queryBuilder->expr()->eq('d.hideonwebsite', 0)
|
||||||
|
)
|
||||||
|
->orderBy('mm.sorting', 'ASC')
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($downloads as $download) {
|
||||||
|
$fileInfo = null;
|
||||||
|
|
||||||
|
// Get file information from FAL if file reference exists
|
||||||
|
if (!empty($download['file'])) {
|
||||||
|
$fileQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('sys_file_reference');
|
||||||
|
|
||||||
|
$fileRef = $fileQueryBuilder
|
||||||
|
->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type')
|
||||||
|
->from('sys_file_reference', 'fr')
|
||||||
|
->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid')
|
||||||
|
->where(
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)),
|
||||||
|
$fileQueryBuilder->expr()->eq('fr.deleted', 0),
|
||||||
|
$fileQueryBuilder->expr()->eq('f.missing', 0)
|
||||||
|
)
|
||||||
|
->orderBy('fr.sorting_foreign', 'ASC')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAssociative();
|
||||||
|
|
||||||
|
if ($fileRef) {
|
||||||
|
$fileInfo = [
|
||||||
|
'uid' => (int)$fileRef['file_uid'],
|
||||||
|
'name' => $fileRef['name'],
|
||||||
|
'url' => '/fileadmin' . $fileRef['identifier'],
|
||||||
|
'size' => (int)$fileRef['size'],
|
||||||
|
'extension' => $fileRef['extension'],
|
||||||
|
'mimeType' => $fileRef['mime_type'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
'uid' => (int)$download['uid'],
|
||||||
|
'title' => $download['title'] ?? '',
|
||||||
|
'slug' => $download['slug'] ?? '',
|
||||||
|
'teaser' => $download['teaser'] ?? '',
|
||||||
|
'description' => $download['description'] ?? '',
|
||||||
|
'keywords' => $download['keywords'] ?? '',
|
||||||
|
'icon' => $download['icon'] ?? '',
|
||||||
|
'file' => $fileInfo,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
122
packages/vitec/Classes/View/BackendLayoutDataProvider.php
Executable file
122
packages/vitec/Classes/View/BackendLayoutDataProvider.php
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\View;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayout\BackendLayout;
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection;
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayout\DataProviderContext;
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayout\DataProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides one Backend Layout per VITEC frontend layout (pages.layout 0..20).
|
||||||
|
*
|
||||||
|
* Backend layout identifiers are the plain layout values: '0', '1', ... '20'.
|
||||||
|
* Combined with the provider id 'vitec' (registered in ext_localconf.php) this
|
||||||
|
* yields the standard combined identifier scheme vitec__<value>.
|
||||||
|
*
|
||||||
|
* The layout's title is shown above the page module and tells the editor
|
||||||
|
* which frontend layout is currently active.
|
||||||
|
*/
|
||||||
|
final class BackendLayoutDataProvider implements DataProviderInterface
|
||||||
|
{
|
||||||
|
/** colPos values across all VITEC backend layouts.
|
||||||
|
* Must NOT collide with container child colPos (>= 211). */
|
||||||
|
private const COL_POS = [
|
||||||
|
'main' => 0,
|
||||||
|
'hero' => 1,
|
||||||
|
'sidebar' => 2,
|
||||||
|
'preFooter' => 3,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Per-frontend-layout: title + ordered list of zones. */
|
||||||
|
public const LAYOUT_MAP = [
|
||||||
|
0 => ['title' => 'Default', 'zones' => ['main']],
|
||||||
|
1 => ['title' => 'Index Page', 'zones' => ['hero', 'main', 'preFooter']],
|
||||||
|
2 => ['title' => 'Markets Overview', 'zones' => ['hero', 'main']],
|
||||||
|
3 => ['title' => 'Market Detail', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
4 => ['title' => 'Solutions Overview', 'zones' => ['hero', 'main']],
|
||||||
|
5 => ['title' => 'Solution Detail', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
6 => ['title' => 'Use Cases Overview', 'zones' => ['hero', 'main']],
|
||||||
|
7 => ['title' => 'Use Case Detail', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
8 => ['title' => 'Products Overview', 'zones' => ['hero', 'main']],
|
||||||
|
9 => ['title' => 'Product Main Category', 'zones' => ['hero', 'main', 'preFooter']],
|
||||||
|
10 => ['title' => 'Product Detail', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
11 => ['title' => 'Support Overview', 'zones' => ['hero', 'main']],
|
||||||
|
12 => ['title' => 'News Overview', 'zones' => ['hero', 'main']],
|
||||||
|
13 => ['title' => 'News Detail V1', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
14 => ['title' => 'News Detail V2', 'zones' => ['hero', 'main']],
|
||||||
|
15 => ['title' => 'News Detail V3', 'zones' => ['hero', 'main', 'sidebar', 'preFooter']],
|
||||||
|
16 => ['title' => 'Content Page V1', 'zones' => ['hero', 'main']],
|
||||||
|
17 => ['title' => 'Content Page V2', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
18 => ['title' => 'Content Page V3', 'zones' => ['hero', 'main', 'sidebar', 'preFooter']],
|
||||||
|
19 => ['title' => 'Events Overview', 'zones' => ['hero', 'main']],
|
||||||
|
20 => ['title' => 'Contact Page', 'zones' => ['hero', 'main', 'sidebar']],
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Display name for each zone. Shown as column header in the BE page module. */
|
||||||
|
private const ZONE_NAMES = [
|
||||||
|
'main' => 'Main Content',
|
||||||
|
'hero' => 'Hero',
|
||||||
|
'sidebar' => 'Sidebar',
|
||||||
|
'preFooter' => 'Pre-Footer',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function addBackendLayouts(DataProviderContext $dataProviderContext, BackendLayoutCollection $backendLayoutCollection)
|
||||||
|
{
|
||||||
|
foreach (self::LAYOUT_MAP as $value => $info) {
|
||||||
|
$backendLayoutCollection->add($this->buildBackendLayout((int)$value, $info['title'], $info['zones']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBackendLayout($identifier, $pageId)
|
||||||
|
{
|
||||||
|
$value = (int)$identifier;
|
||||||
|
if (!isset(self::LAYOUT_MAP[$value])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$info = self::LAYOUT_MAP[$value];
|
||||||
|
return $this->buildBackendLayout($value, $info['title'], $info['zones']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $zones
|
||||||
|
*/
|
||||||
|
private function buildBackendLayout(int $value, string $title, array $zones): BackendLayout
|
||||||
|
{
|
||||||
|
$rowsTs = '';
|
||||||
|
$rowNum = 1;
|
||||||
|
foreach ($zones as $zoneKey) {
|
||||||
|
$name = self::ZONE_NAMES[$zoneKey];
|
||||||
|
$colPos = self::COL_POS[$zoneKey];
|
||||||
|
$rowsTs .= <<<TS
|
||||||
|
{$rowNum} {
|
||||||
|
columns {
|
||||||
|
1 {
|
||||||
|
name = {$name}
|
||||||
|
colPos = {$colPos}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TS;
|
||||||
|
$rowNum++;
|
||||||
|
}
|
||||||
|
$rowCount = count($zones);
|
||||||
|
$configuration = <<<TS
|
||||||
|
backend_layout {
|
||||||
|
colCount = 1
|
||||||
|
rowCount = {$rowCount}
|
||||||
|
rows {
|
||||||
|
{$rowsTs} }
|
||||||
|
}
|
||||||
|
TS;
|
||||||
|
|
||||||
|
// Identifier is plain numeric; combined form becomes "vitec__<value>"
|
||||||
|
return new BackendLayout(
|
||||||
|
(string)$value,
|
||||||
|
'VITEC Layout: ' . $title,
|
||||||
|
$configuration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
packages/vitec/Classes/View/ProductListJsonView.php
Normal file
58
packages/vitec/Classes/View/ProductListJsonView.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\View;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\View\AbstractView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON View for Headless API
|
||||||
|
*/
|
||||||
|
class ProductListJsonView extends AbstractView
|
||||||
|
{
|
||||||
|
public function render(): string
|
||||||
|
{
|
||||||
|
$products = $this->variables['products'] ?? [];
|
||||||
|
$productsData = [];
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
$categories = [];
|
||||||
|
if ($product->getCategories()) {
|
||||||
|
foreach ($product->getCategories() as $category) {
|
||||||
|
$categories[] = [
|
||||||
|
'uid' => $category->getUid(),
|
||||||
|
'title' => $category->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = null;
|
||||||
|
if ($product->getProductimage() && $product->getProductimage()->count() > 0) {
|
||||||
|
$imageObject = $product->getProductimage()->current();
|
||||||
|
$image = [
|
||||||
|
'uid' => $imageObject->getUid(),
|
||||||
|
'url' => $imageObject->getOriginalResource()->getPublicUrl(),
|
||||||
|
'title' => $imageObject->getTitle(),
|
||||||
|
'alternative' => $imageObject->getAlternative(),
|
||||||
|
'description' => $imageObject->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$productsData[] = [
|
||||||
|
'uid' => $product->getUid(),
|
||||||
|
'title' => $product->getTitle(),
|
||||||
|
'subtitle' => $product->getSubtitle(),
|
||||||
|
'teaser' => $product->getTeaser(),
|
||||||
|
'description' => $product->getDescription(),
|
||||||
|
'slug' => $product->getSlug(),
|
||||||
|
'categories' => $categories,
|
||||||
|
'image' => $image,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode([
|
||||||
|
'products' => $productsData,
|
||||||
|
], JSON_THROW_ON_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
packages/vitec/Classes/View/VitecBackendLayoutView.php
Executable file
29
packages/vitec/Classes/View/VitecBackendLayoutView.php
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Evomedien\Vitec\View;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||||
|
use TYPO3\CMS\Backend\View\BackendLayoutView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides BackendLayoutView so the page module always reflects pages.layout
|
||||||
|
* without the editor having to set backend_layout manually.
|
||||||
|
*/
|
||||||
|
final class VitecBackendLayoutView extends BackendLayoutView
|
||||||
|
{
|
||||||
|
public function getSelectedCombinedIdentifier(int $pageId): string|false
|
||||||
|
{
|
||||||
|
if ($pageId <= 0) {
|
||||||
|
return parent::getSelectedCombinedIdentifier($pageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = BackendUtility::getRecord('pages', $pageId, 'layout');
|
||||||
|
if (!is_array($page) || !isset($page['layout'])) {
|
||||||
|
return parent::getSelectedCombinedIdentifier($pageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combined identifier format: "<dataProvider>__<layoutId>"
|
||||||
|
return 'vitec__' . (int)$page['layout'];
|
||||||
|
}
|
||||||
|
}
|
||||||
38
packages/vitec/Classes/Widgets/VitecWidget.php
Normal file
38
packages/vitec/Classes/Widgets/VitecWidget.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
namespace Evomedien\Vitec\Widgets;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Dashboard\Widgets\WidgetInterface;
|
||||||
|
use TYPO3\CMS\Dashboard\Widgets\WidgetConfiguration;
|
||||||
|
|
||||||
|
class VitecWidget implements WidgetInterface
|
||||||
|
{
|
||||||
|
private WidgetConfiguration $configuration;
|
||||||
|
|
||||||
|
public function __construct(WidgetConfiguration $configuration)
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): string
|
||||||
|
{
|
||||||
|
// Render the widget container
|
||||||
|
return '<div class="vitec-widget">' . $this->renderWidgetContent() . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderWidgetContent(): string
|
||||||
|
{
|
||||||
|
// Render the actual widget content
|
||||||
|
return '<div>Welcome to the Vitec custom widget!</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptions(): array
|
||||||
|
{
|
||||||
|
// Return widget options (if any)
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfiguration(): WidgetConfiguration
|
||||||
|
{
|
||||||
|
return $this->configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/vitec/Configuration/ContentBlocks.yaml
Normal file
4
packages/vitec/Configuration/ContentBlocks.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
contentBlocks:
|
||||||
|
paths:
|
||||||
|
# Register all content elements under ContentBlocks/ContentElements
|
||||||
|
- 'EXT:vitec/ContentBlocks/ContentElements/'
|
||||||
103
packages/vitec/Configuration/ExtensionBuilder/settings.yaml
Normal file
103
packages/vitec/Configuration/ExtensionBuilder/settings.yaml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#
|
||||||
|
# Extension Builder settings for extension vitec
|
||||||
|
# generated 2025-01-13T15:54:51Z
|
||||||
|
#
|
||||||
|
# See http://www.yaml.org/spec/1.2/spec.html
|
||||||
|
#
|
||||||
|
|
||||||
|
############# Overwrite settings ###########
|
||||||
|
#
|
||||||
|
# These settings only apply, if the roundtrip feature of the extension builder
|
||||||
|
# is enabled in the extension manager
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# nesting reflects the file structure
|
||||||
|
# a setting applies to a file or recursive to all files and subfolders
|
||||||
|
#
|
||||||
|
# merge:
|
||||||
|
# means for classes: All properties, methods and method bodies
|
||||||
|
# of the existing class will be modified according to the new settings
|
||||||
|
# but not overwritten
|
||||||
|
#
|
||||||
|
# for locallang.xlf files: Existing keys and labels are always
|
||||||
|
# preserved (renaming a property or DomainObject will result in new keys and new labels)
|
||||||
|
#
|
||||||
|
# for other files: You will find a Split token at the end of the file
|
||||||
|
# see: \EBT\ExtensionBuilder\Service\RoundTrip::SPLIT_TOKEN
|
||||||
|
#
|
||||||
|
# After this token you can write whatever you want and it will be appended
|
||||||
|
# everytime the code is generated
|
||||||
|
#
|
||||||
|
# keep:
|
||||||
|
# files are never overwritten
|
||||||
|
# These settings may break the functionality of the extension builder!
|
||||||
|
# Handle with care!
|
||||||
|
|
||||||
|
############# Extension settings ###########
|
||||||
|
|
||||||
|
overwriteSettings:
|
||||||
|
Classes:
|
||||||
|
Controller: merge
|
||||||
|
Domain:
|
||||||
|
Model: merge
|
||||||
|
Repository: merge
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
# TCA merge not possible - use overrides directory
|
||||||
|
#TypoScript: keep
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
Private:
|
||||||
|
#Language: merge
|
||||||
|
#Layouts: keep
|
||||||
|
#Partials: keep
|
||||||
|
#Templates: keep
|
||||||
|
Backend:
|
||||||
|
#Layouts: keep
|
||||||
|
#Partials: keep
|
||||||
|
#Templates: keep
|
||||||
|
|
||||||
|
user_extension.svg: keep
|
||||||
|
#ext_localconf.php: merge
|
||||||
|
#ext_tables.php: merge
|
||||||
|
#ext_tables.sql: merge
|
||||||
|
|
||||||
|
## add declare strict types in php files
|
||||||
|
declareStrictTypes: true
|
||||||
|
|
||||||
|
## use static date attribute in xliff files
|
||||||
|
#staticDateInXliffFiles: '2025-01-13T15:54:51Z'
|
||||||
|
|
||||||
|
## skip docComment (license header)
|
||||||
|
#skipDocComment: false
|
||||||
|
|
||||||
|
## list of error codes for warnings that should be ignored
|
||||||
|
#ignoreWarnings:
|
||||||
|
#503
|
||||||
|
|
||||||
|
############# settings for classBuilder #######################
|
||||||
|
#
|
||||||
|
# here you may define default parent classes for your classes
|
||||||
|
# these settings only apply for new generated classes
|
||||||
|
# you may also just change the parent class in the generated class file.
|
||||||
|
# It will be kept on next code generation, if the overwrite settings
|
||||||
|
# are configured to merge it
|
||||||
|
#
|
||||||
|
#################################################################
|
||||||
|
|
||||||
|
classBuilder:
|
||||||
|
|
||||||
|
Controller:
|
||||||
|
parentClass: \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
|
||||||
|
|
||||||
|
Model:
|
||||||
|
AbstractEntity:
|
||||||
|
parentClass: \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
|
||||||
|
|
||||||
|
AbstractValueObject:
|
||||||
|
parentClass: \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject
|
||||||
|
|
||||||
|
Repository:
|
||||||
|
parentClass: \TYPO3\CMS\Extbase\Persistence\Repository
|
||||||
|
|
||||||
|
setDefaultValuesForClassProperties: true
|
||||||
65
packages/vitec/Configuration/FlexForms/Datasheets.xml
Normal file
65
packages/vitec/Configuration/FlexForms/Datasheets.xml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<meta>
|
||||||
|
<langDisable>1</langDisable>
|
||||||
|
</meta>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<TCEforms>
|
||||||
|
<sheetTitle>General Settings</sheetTitle>
|
||||||
|
</TCEforms>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.showFilter>
|
||||||
|
<TCEforms>
|
||||||
|
<label>Show Filter</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>1</default>
|
||||||
|
</config>
|
||||||
|
</TCEforms>
|
||||||
|
</settings.showFilter>
|
||||||
|
|
||||||
|
<settings.showSearch>
|
||||||
|
<TCEforms>
|
||||||
|
<label>Show Search</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>1</default>
|
||||||
|
</config>
|
||||||
|
</TCEforms>
|
||||||
|
</settings.showSearch>
|
||||||
|
|
||||||
|
<settings.itemsPerPage>
|
||||||
|
<TCEforms>
|
||||||
|
<label>Items per Page</label>
|
||||||
|
<config>
|
||||||
|
<type>number</type>
|
||||||
|
<default>20</default>
|
||||||
|
</config>
|
||||||
|
</TCEforms>
|
||||||
|
</settings.itemsPerPage>
|
||||||
|
|
||||||
|
<settings.defaultCategory>
|
||||||
|
<TCEforms>
|
||||||
|
<label>Default Category</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<foreign_table>tx_vitec_domain_model_downloadcategory</foreign_table>
|
||||||
|
<foreign_table_where>ORDER BY title</foreign_table_where>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>All Categories</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</TCEforms>
|
||||||
|
</settings.defaultCategory>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
119
packages/vitec/Configuration/FlexForms/Downloadcard.xml
Normal file
119
packages/vitec/Configuration/FlexForms/Downloadcard.xml
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<TCEforms>
|
||||||
|
<sheetTitle>General Settings</sheetTitle>
|
||||||
|
</TCEforms>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.product>
|
||||||
|
<label>Select Product</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>None</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<foreign_table>tx_vitec_domain_model_product</foreign_table>
|
||||||
|
<foreign_table_where>AND (tx_vitec_domain_model_product.hidden = 0 AND tx_vitec_domain_model_product.deleted = 0) ORDER BY tx_vitec_domain_model_product.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>99</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.product>
|
||||||
|
<settings.download>
|
||||||
|
<label>Select Download</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>None</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<foreign_table>tx_vitec_domain_model_download</foreign_table>
|
||||||
|
<foreign_table_where>AND (tx_vitec_domain_model_download.hidden = 0 AND tx_vitec_domain_model_download.deleted = 0) ORDER BY tx_vitec_domain_model_download.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>99</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.download>
|
||||||
|
<settings.layout>
|
||||||
|
<label>Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>
|
||||||
|
Default Product List
|
||||||
|
</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>
|
||||||
|
Variation 1 Product List
|
||||||
|
</label>
|
||||||
|
<value>1</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>
|
||||||
|
Variation 2 Product List
|
||||||
|
</label>
|
||||||
|
<value>2</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>
|
||||||
|
Variation 3 Product List
|
||||||
|
</label>
|
||||||
|
<value>3</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
<settings.magstyle>
|
||||||
|
<label>Magazine Style layout</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>0</default>
|
||||||
|
</config>
|
||||||
|
</settings.magstyle>
|
||||||
|
<settings.adddetaillink>
|
||||||
|
<label>Add Button to Detail View</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>0</default>
|
||||||
|
</config>
|
||||||
|
</settings.adddetaillink>
|
||||||
|
<settings.magheader>
|
||||||
|
<label>Header Text for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.magheader>
|
||||||
|
<settings.magtext>
|
||||||
|
<label>A short Text for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.magtext>
|
||||||
|
<settings.maglink>
|
||||||
|
<label>Link for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.maglink>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<TCEforms>
|
||||||
|
<sheetTitle>General Settings</sheetTitle>
|
||||||
|
</TCEforms>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.image>
|
||||||
|
<label>Image</label>
|
||||||
|
<config>
|
||||||
|
<type>inline</type>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
<foreign_table>sys_file_reference</foreign_table>
|
||||||
|
<foreign_table_field>tablenames</foreign_table_field>
|
||||||
|
<foreign_label>uid_local</foreign_label>
|
||||||
|
<foreign_sortby>sorting_foreign</foreign_sortby>
|
||||||
|
<foreign_field>uid_foreign</foreign_field>
|
||||||
|
<foreign_selector>uid_local</foreign_selector>
|
||||||
|
<foreign_selector_fieldTcaOverride>
|
||||||
|
<config>
|
||||||
|
<appearance>
|
||||||
|
<elementBrowserType>file</elementBrowserType>
|
||||||
|
<elementBrowserAllowed>gif,jpg,jpeg,png,svg</elementBrowserAllowed>
|
||||||
|
</appearance>
|
||||||
|
</config>
|
||||||
|
</foreign_selector_fieldTcaOverride>
|
||||||
|
<foreign_types type="array">
|
||||||
|
<numIndex index="0">
|
||||||
|
<showitem>--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,--palette--;;filePalette</showitem>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<showitem>--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,--palette--;;filePalette</showitem>
|
||||||
|
</numIndex>
|
||||||
|
</foreign_types>
|
||||||
|
<foreign_match_fields>
|
||||||
|
<fieldname>image</fieldname> <!-- CAUTION!! Replace "fal" with the variable name of this field! -->
|
||||||
|
</foreign_match_fields>
|
||||||
|
<appearance type="array">
|
||||||
|
<newRecordLinkAddTitle>1</newRecordLinkAddTitle>
|
||||||
|
<headerThumbnail>
|
||||||
|
<field>uid_local</field>
|
||||||
|
<height>64</height>
|
||||||
|
<width>64</width>
|
||||||
|
</headerThumbnail>
|
||||||
|
<enabledControls>
|
||||||
|
<info>1</info>
|
||||||
|
<new>0</new>
|
||||||
|
<dragdrop>0</dragdrop>
|
||||||
|
<sort>1</sort>
|
||||||
|
<hide>0</hide>
|
||||||
|
<delete>1</delete>
|
||||||
|
<localize>1</localize>
|
||||||
|
</enabledControls>
|
||||||
|
<createNewRelationLinkTitle>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference</createNewRelationLinkTitle>
|
||||||
|
</appearance>
|
||||||
|
<behaviour>
|
||||||
|
<localizationMode>select</localizationMode>
|
||||||
|
<localizeChildrenAtParentLocalization>1</localizeChildrenAtParentLocalization>
|
||||||
|
</behaviour>
|
||||||
|
<overrideChildTca>
|
||||||
|
<columns type="array">
|
||||||
|
<uid_local type="array">
|
||||||
|
<config type="array">
|
||||||
|
<appearance type="array">
|
||||||
|
<elementBrowserType>file</elementBrowserType>
|
||||||
|
<elementBrowserAllowed>jpg,png,svg,jpeg,gif</elementBrowserAllowed>
|
||||||
|
</appearance>
|
||||||
|
</config>
|
||||||
|
</uid_local>
|
||||||
|
</columns>
|
||||||
|
<types type="array">
|
||||||
|
<numIndex index="2">
|
||||||
|
<showitem>--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,--palette--;;filePalette</showitem>
|
||||||
|
</numIndex>
|
||||||
|
</types>
|
||||||
|
</overrideChildTca>
|
||||||
|
</config>
|
||||||
|
</settings.image>
|
||||||
|
<settings.download>
|
||||||
|
<label>Select Download</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectMultipleSideBySide</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>None</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<foreign_table>tx_vitec_domain_model_download</foreign_table>
|
||||||
|
<foreign_table_where>AND (tx_vitec_domain_model_download.hidden = 0 AND tx_vitec_domain_model_download.deleted = 0) ORDER BY tx_vitec_domain_model_download.title</foreign_table_where>
|
||||||
|
<size>4</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>99</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.download>
|
||||||
|
<settings.layout>
|
||||||
|
<label>Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>
|
||||||
|
Default Product List
|
||||||
|
</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>
|
||||||
|
Variation 1 Product List
|
||||||
|
</label>
|
||||||
|
<value>1</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>
|
||||||
|
Variation 2 Product List
|
||||||
|
</label>
|
||||||
|
<value>2</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>
|
||||||
|
Variation 3 Product List
|
||||||
|
</label>
|
||||||
|
<value>3</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
<settings.magstyle>
|
||||||
|
<label>Magazine Style layout</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>0</default>
|
||||||
|
</config>
|
||||||
|
</settings.magstyle>
|
||||||
|
<settings.adddetaillink>
|
||||||
|
<label>Add Button to Detail View</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<default>0</default>
|
||||||
|
</config>
|
||||||
|
</settings.adddetaillink>
|
||||||
|
<settings.magheader>
|
||||||
|
<label>Header Text for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.magheader>
|
||||||
|
<settings.magtext>
|
||||||
|
<label>A short Text for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.magtext>
|
||||||
|
<settings.maglink>
|
||||||
|
<label>Link for Magazine Layout</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.maglink>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
69
packages/vitec/Configuration/FlexForms/Market.xml
Normal file
69
packages/vitec/Configuration/FlexForms/Market.xml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Market
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.debug>
|
||||||
|
<label>Allow Debug Output.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.debug>
|
||||||
|
<settings.market>
|
||||||
|
<label>
|
||||||
|
Market
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<foreign_table>tx_vitec_domain_model_market</foreign_table>
|
||||||
|
<foreign_table_where>AND tx_vitec_domain_model_market.hidden = 0 AND tx_vitec_domain_model_market.deleted = 0 ORDER BY tx_vitec_domain_model_market.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.market>
|
||||||
|
<settings.layout>
|
||||||
|
<label>
|
||||||
|
Layout
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>Default Layout</label>
|
||||||
|
<value>default</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>Card Layout</label>
|
||||||
|
<value>card</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>Hero Layout</label>
|
||||||
|
<value>hero</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>Compact Layout</label>
|
||||||
|
<value>compact</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<default>default</default>
|
||||||
|
<size>1</size>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
99
packages/vitec/Configuration/FlexForms/Productlist.xml
Normal file
99
packages/vitec/Configuration/FlexForms/Productlist.xml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Categories
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.allproducts>
|
||||||
|
<label>Show all products disregarding the category selection below.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.allproducts>
|
||||||
|
<settings.debug>
|
||||||
|
<label>Allow Debug Output.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.debug>
|
||||||
|
<settings.categories>
|
||||||
|
<label>Select Category</label>
|
||||||
|
<description>
|
||||||
|
Select one or more categories to filter the products in the product list.
|
||||||
|
|
||||||
|
</description>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderMode>tree</renderMode>
|
||||||
|
<renderType>selectTree</renderType>
|
||||||
|
<treeConfig>
|
||||||
|
<!-- <dataProvider>GeorgRinger\News\TreeProvider\DatabaseTreeDataProvider</dataProvider>-->
|
||||||
|
<parentField>parent</parentField>
|
||||||
|
<appearance>
|
||||||
|
<maxLevels>99</maxLevels>
|
||||||
|
<expandAll>TRUE</expandAll>
|
||||||
|
<showHeader>TRUE</showHeader>
|
||||||
|
</appearance>
|
||||||
|
</treeConfig>
|
||||||
|
<foreign_table>sys_category</foreign_table>
|
||||||
|
<foreign_table_where>AND (sys_category.sys_language_uid = 0 OR sys_category.l10n_parent = 0) ORDER BY sys_category.sorting</foreign_table_where>
|
||||||
|
<size>15</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>99</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.categories>
|
||||||
|
<settings.layout>
|
||||||
|
<label>Layout</label>
|
||||||
|
<description>
|
||||||
|
Select which layout variation should be used for the product list in the frontend.
|
||||||
|
</description>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>
|
||||||
|
Default Product List
|
||||||
|
</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>
|
||||||
|
Variation 1 Product List
|
||||||
|
</label>
|
||||||
|
<value>1</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>
|
||||||
|
Variation 2 Product List
|
||||||
|
</label>
|
||||||
|
<value>2</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>
|
||||||
|
Variation 3 Product List
|
||||||
|
</label>
|
||||||
|
<value>3</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
82
packages/vitec/Configuration/FlexForms/Productshow.xml
Normal file
82
packages/vitec/Configuration/FlexForms/Productshow.xml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Product
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.debug>
|
||||||
|
<label>Allow Debug Output.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.debug>
|
||||||
|
<settings.product>
|
||||||
|
<label>
|
||||||
|
Select Product
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>No Product - Use product from URL parameter</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<foreign_table>tx_vitec_domain_model_product</foreign_table>
|
||||||
|
<foreign_table_where>AND tx_vitec_domain_model_product.hidden = 0 AND tx_vitec_domain_model_product.deleted = 0 ORDER BY tx_vitec_domain_model_product.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.product>
|
||||||
|
<settings.layout>
|
||||||
|
<label>Layout</label>
|
||||||
|
<description>
|
||||||
|
Select which layout variation should be used for the product in the frontend.
|
||||||
|
</description>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>
|
||||||
|
Default Product Card
|
||||||
|
</label>
|
||||||
|
<value>0</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>
|
||||||
|
Variation 1 Product Card
|
||||||
|
</label>
|
||||||
|
<value>1</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>
|
||||||
|
Variation 2 Product Card
|
||||||
|
</label>
|
||||||
|
<value>2</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>
|
||||||
|
Variation 3 Product Card
|
||||||
|
</label>
|
||||||
|
<value>3</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
66
packages/vitec/Configuration/FlexForms/Simplecard.xml
Normal file
66
packages/vitec/Configuration/FlexForms/Simplecard.xml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>Simple Card Settings</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.header>
|
||||||
|
<label>Header</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>48</size>
|
||||||
|
</config>
|
||||||
|
</settings.header>
|
||||||
|
<settings.bodytext>
|
||||||
|
<label>Text</label>
|
||||||
|
<config>
|
||||||
|
<type>text</type>
|
||||||
|
<enableRichtext>1</enableRichtext>
|
||||||
|
<richtextConfiguration>default</richtextConfiguration>
|
||||||
|
<cols>48</cols>
|
||||||
|
<rows>5</rows>
|
||||||
|
</config>
|
||||||
|
</settings.bodytext>
|
||||||
|
<settings.image>
|
||||||
|
<label>Image</label>
|
||||||
|
<config>
|
||||||
|
<type>file</type>
|
||||||
|
<appearance>
|
||||||
|
<createNewRelationLinkTitle>Add Image</createNewRelationLinkTitle>
|
||||||
|
</appearance>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<allowed>jpg,jpeg,png,gif,svg</allowed>
|
||||||
|
<fieldControl>
|
||||||
|
<editPopup>
|
||||||
|
<disabled>0</disabled>
|
||||||
|
</editPopup>
|
||||||
|
<addFileReference>
|
||||||
|
<disabled>0</disabled>
|
||||||
|
</addFileReference>
|
||||||
|
</fieldControl>
|
||||||
|
</config>
|
||||||
|
</settings.image>
|
||||||
|
<settings.buttontext>
|
||||||
|
<label>Button Text</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<size>30</size>
|
||||||
|
</config>
|
||||||
|
</settings.buttontext>
|
||||||
|
<settings.buttonlink>
|
||||||
|
<label>Button Link</label>
|
||||||
|
<config>
|
||||||
|
<type>input</type>
|
||||||
|
<renderType>inputLink</renderType>
|
||||||
|
<size>30</size>
|
||||||
|
<eval>trim</eval>
|
||||||
|
</config>
|
||||||
|
</settings.buttonlink>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
69
packages/vitec/Configuration/FlexForms/Solution.xml
Normal file
69
packages/vitec/Configuration/FlexForms/Solution.xml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Solution
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.debug>
|
||||||
|
<label>Allow Debug Output.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.debug>
|
||||||
|
<settings.solution>
|
||||||
|
<label>
|
||||||
|
Solution
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<foreign_table>tx_vitec_domain_model_solution</foreign_table>
|
||||||
|
<foreign_table_where>AND tx_vitec_domain_model_solution.hidden = 0 AND tx_vitec_domain_model_solution.deleted = 0 ORDER BY tx_vitec_domain_model_solution.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.solution>
|
||||||
|
<settings.layout>
|
||||||
|
<label>
|
||||||
|
Layout
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>Default Layout</label>
|
||||||
|
<value>default</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>Card Layout</label>
|
||||||
|
<value>card</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>Hero Layout</label>
|
||||||
|
<value>hero</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>Compact Layout</label>
|
||||||
|
<value>compact</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<default>default</default>
|
||||||
|
<size>1</size>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
69
packages/vitec/Configuration/FlexForms/Usecase.xml
Normal file
69
packages/vitec/Configuration/FlexForms/Usecase.xml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Usecase
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.debug>
|
||||||
|
<label>Allow Debug Output.</label>
|
||||||
|
<config>
|
||||||
|
<type>check</type>
|
||||||
|
<items type="array">
|
||||||
|
<numIndex index="0" type="array">
|
||||||
|
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
</config>
|
||||||
|
</settings.debug>
|
||||||
|
<settings.usecase>
|
||||||
|
<label>
|
||||||
|
Usecase
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<foreign_table>tx_vitec_domain_model_usecase</foreign_table>
|
||||||
|
<foreign_table_where>AND tx_vitec_domain_model_usecase.hidden = 0 AND tx_vitec_domain_model_usecase.deleted = 0 ORDER BY tx_vitec_domain_model_usecase.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.usecase>
|
||||||
|
<settings.layout>
|
||||||
|
<label>
|
||||||
|
Layout
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<items>
|
||||||
|
<numIndex index="0">
|
||||||
|
<label>Default Layout</label>
|
||||||
|
<value>default</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="1">
|
||||||
|
<label>Card Layout</label>
|
||||||
|
<value>card</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="2">
|
||||||
|
<label>Hero Layout</label>
|
||||||
|
<value>hero</value>
|
||||||
|
</numIndex>
|
||||||
|
<numIndex index="3">
|
||||||
|
<label>Compact Layout</label>
|
||||||
|
<value>compact</value>
|
||||||
|
</numIndex>
|
||||||
|
</items>
|
||||||
|
<default>default</default>
|
||||||
|
<size>1</size>
|
||||||
|
</config>
|
||||||
|
</settings.layout>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
29
packages/vitec/Configuration/FlexForms/Usecaselist.xml
Normal file
29
packages/vitec/Configuration/FlexForms/Usecaselist.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<T3DataStructure>
|
||||||
|
<sheets>
|
||||||
|
<sDEF>
|
||||||
|
<ROOT>
|
||||||
|
<sheetTitle>
|
||||||
|
Select Usecase
|
||||||
|
</sheetTitle>
|
||||||
|
<type>array</type>
|
||||||
|
<el>
|
||||||
|
<settings.usecase>
|
||||||
|
<label>
|
||||||
|
Usecase
|
||||||
|
</label>
|
||||||
|
<config>
|
||||||
|
<type>select</type>
|
||||||
|
<renderType>selectSingle</renderType>
|
||||||
|
<foreign_table>tx_vitec_domain_model_usecase</foreign_table>
|
||||||
|
<foreign_table_where>AND tx_vitec_domain_model_usecase.hidden = 0 AND tx_vitec_domain_model_usecase.deleted = 0 ORDER BY tx_vitec_domain_model_usecase.title</foreign_table_where>
|
||||||
|
<size>1</size>
|
||||||
|
<minitems>0</minitems>
|
||||||
|
<maxitems>1</maxitems>
|
||||||
|
</config>
|
||||||
|
</settings.usecase>
|
||||||
|
</el>
|
||||||
|
</ROOT>
|
||||||
|
</sDEF>
|
||||||
|
</sheets>
|
||||||
|
</T3DataStructure>
|
||||||
27
packages/vitec/Configuration/Icons.php
Executable file
27
packages/vitec/Configuration/Icons.php
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'vitec-cols-50-50' => [
|
||||||
|
'provider' => SvgIconProvider::class,
|
||||||
|
'source' => 'EXT:vitec/Resources/Public/Icons/vitec-cols-50-50.svg',
|
||||||
|
],
|
||||||
|
'vitec-cols-33-33-33' => [
|
||||||
|
'provider' => SvgIconProvider::class,
|
||||||
|
'source' => 'EXT:vitec/Resources/Public/Icons/vitec-cols-33-33-33.svg',
|
||||||
|
],
|
||||||
|
'vitec-cols-25-25-25-25' => [
|
||||||
|
'provider' => SvgIconProvider::class,
|
||||||
|
'source' => 'EXT:vitec/Resources/Public/Icons/vitec-cols-25-25-25-25.svg',
|
||||||
|
],
|
||||||
|
'vitec-cols-66-33' => [
|
||||||
|
'provider' => SvgIconProvider::class,
|
||||||
|
'source' => 'EXT:vitec/Resources/Public/Icons/vitec-cols-66-33.svg',
|
||||||
|
],
|
||||||
|
'vitec-cols-33-66' => [
|
||||||
|
'provider' => SvgIconProvider::class,
|
||||||
|
'source' => 'EXT:vitec/Resources/Public/Icons/vitec-cols-33-66.svg',
|
||||||
|
],
|
||||||
|
];
|
||||||
199
packages/vitec/Configuration/RTE/Vitec.yaml
Normal file
199
packages/vitec/Configuration/RTE/Vitec.yaml
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# ============================================================
|
||||||
|
# VITEC — Full CKEditor5 Preset
|
||||||
|
# All available toolbar items, styles, fonts, and options
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Processing.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml' }
|
||||||
|
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
# Load frontend-like classes into RTE iframe for live style preview
|
||||||
|
contentsCss:
|
||||||
|
- 'EXT:rte_ckeditor/Resources/Public/Css/contents.css'
|
||||||
|
- 'EXT:vitec/Resources/Public/Css/rte-content.css'
|
||||||
|
|
||||||
|
# ---- Toolbar (all available items) ----------------------
|
||||||
|
toolbar:
|
||||||
|
shouldNotGroupWhenFull: true
|
||||||
|
items:
|
||||||
|
# Undo / Redo / Cleanup
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
|
- removeFormat
|
||||||
|
- selectAll
|
||||||
|
- '|'
|
||||||
|
# Find & Replace / Source
|
||||||
|
- findAndReplace
|
||||||
|
- sourceEditing
|
||||||
|
- showBlocks
|
||||||
|
- fullscreen
|
||||||
|
- '|'
|
||||||
|
# Headings & Styles
|
||||||
|
- heading
|
||||||
|
- style
|
||||||
|
- '-'
|
||||||
|
# Basic formatting
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- underline
|
||||||
|
- strikethrough
|
||||||
|
- subscript
|
||||||
|
- superscript
|
||||||
|
- softhyphen
|
||||||
|
- '|'
|
||||||
|
# Font
|
||||||
|
- fontFamily
|
||||||
|
- fontSize
|
||||||
|
- fontColor
|
||||||
|
- fontBackgroundColor
|
||||||
|
- highlight
|
||||||
|
- highlight:greenMarker
|
||||||
|
- '-'
|
||||||
|
# Lists & Indent
|
||||||
|
- bulletedList
|
||||||
|
- numberedList
|
||||||
|
- blockQuote
|
||||||
|
- indent
|
||||||
|
- outdent
|
||||||
|
- alignment
|
||||||
|
- horizontalLine
|
||||||
|
- '|'
|
||||||
|
# Links & Media
|
||||||
|
- link
|
||||||
|
- '|'
|
||||||
|
# Tables
|
||||||
|
- insertTable
|
||||||
|
- tableColumn
|
||||||
|
- tableRow
|
||||||
|
- mergeTableCells
|
||||||
|
- TableProperties
|
||||||
|
- TableCellProperties
|
||||||
|
- '-'
|
||||||
|
# Special
|
||||||
|
- specialCharacters
|
||||||
|
- textPartLanguage
|
||||||
|
|
||||||
|
# ---- Heading options ------------------------------------
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'paragraph', title: 'Paragraph' }
|
||||||
|
- { model: 'heading1', view: 'h1', title: 'Heading 1' }
|
||||||
|
- { model: 'heading2', view: 'h2', title: 'Heading 2' }
|
||||||
|
- { model: 'heading3', view: 'h3', title: 'Heading 3' }
|
||||||
|
- { model: 'heading4', view: 'h4', title: 'Heading 4' }
|
||||||
|
- { model: 'heading5', view: 'h5', title: 'Heading 5' }
|
||||||
|
- { model: 'heading6', view: 'h6', title: 'Heading 6' }
|
||||||
|
- { model: 'formatted', view: 'pre', title: 'Pre-Formatted Text' }
|
||||||
|
|
||||||
|
# ---- Style definitions ----------------------------------
|
||||||
|
style:
|
||||||
|
definitions:
|
||||||
|
# Block level
|
||||||
|
- { name: 'Lead / Intro', element: 'p', classes: ['lead'] }
|
||||||
|
- { name: 'Quote / Citation', element: 'blockquote', classes: ['blockquote'] }
|
||||||
|
- { name: 'Code block', element: 'code' }
|
||||||
|
# Buttons (VITEC color system)
|
||||||
|
- { name: 'Button: Orange', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--orange'] }
|
||||||
|
- { name: 'Button: Blue', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--blue'] }
|
||||||
|
- { name: 'Button: Graphite', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--graphite'] }
|
||||||
|
- { name: 'Button: Midnight', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--midnight'] }
|
||||||
|
- { name: 'Button: Light', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--light'] }
|
||||||
|
- { name: 'Button: Outline Orange', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--outline-orange'] }
|
||||||
|
- { name: 'Button: Outline Blue', element: 'a', classes: ['btn', 'btn-vitec', 'btn-vitec--outline-blue'] }
|
||||||
|
# Inline
|
||||||
|
- { name: 'Highlight yellow', element: 'mark', classes: ['highlight-yellow'] }
|
||||||
|
- { name: 'Highlight green', element: 'mark', classes: ['highlight-green'] }
|
||||||
|
- { name: 'Small text', element: 'small' }
|
||||||
|
- { name: 'Keyboard input', element: 'kbd' }
|
||||||
|
- { name: 'Delete / Strike', element: 'del' }
|
||||||
|
- { name: 'Insert / Underline', element: 'ins' }
|
||||||
|
|
||||||
|
# ---- Alignment -----------------------------------------
|
||||||
|
alignment:
|
||||||
|
options:
|
||||||
|
- { name: 'left', className: 'text-start' }
|
||||||
|
- { name: 'center', className: 'text-center' }
|
||||||
|
- { name: 'right', className: 'text-end' }
|
||||||
|
- { name: 'justify', className: 'text-justify' }
|
||||||
|
|
||||||
|
# ---- Table defaults ------------------------------------
|
||||||
|
table:
|
||||||
|
defaultHeadings: { rows: 1 }
|
||||||
|
contentToolbar:
|
||||||
|
- tableColumn
|
||||||
|
- tableRow
|
||||||
|
- mergeTableCells
|
||||||
|
- tableProperties
|
||||||
|
- tableCellProperties
|
||||||
|
- toggleTableCaption
|
||||||
|
|
||||||
|
# ---- Font family ----------------------------------------
|
||||||
|
fontFamily:
|
||||||
|
supportAllValues: false
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 'Arial, sans-serif'
|
||||||
|
- 'Georgia, serif'
|
||||||
|
- 'Courier New, monospace'
|
||||||
|
|
||||||
|
# ---- Font size ------------------------------------------
|
||||||
|
fontSize:
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 12
|
||||||
|
- 14
|
||||||
|
- 16
|
||||||
|
- 18
|
||||||
|
- 20
|
||||||
|
- 24
|
||||||
|
- 28
|
||||||
|
- 32
|
||||||
|
- 36
|
||||||
|
- 48
|
||||||
|
|
||||||
|
# ---- Font color -----------------------------------------
|
||||||
|
fontColor:
|
||||||
|
columns: 6
|
||||||
|
colors:
|
||||||
|
- { label: 'VITEC Orange', color: '#F47937' }
|
||||||
|
- { label: 'VITEC Blue', color: '#26358C' }
|
||||||
|
- { label: 'VITEC Graphite', color: '#313131' }
|
||||||
|
- { label: 'VITEC Midnight', color: '#0D0D0D' }
|
||||||
|
- { label: 'VITEC Light', color: '#CCCCCC' }
|
||||||
|
- { label: 'Black', color: '#000000' }
|
||||||
|
- { label: 'White', color: '#FFFFFF' }
|
||||||
|
|
||||||
|
# ---- Font background color ------------------------------
|
||||||
|
fontBackgroundColor:
|
||||||
|
columns: 6
|
||||||
|
colors:
|
||||||
|
- { label: 'VITEC Orange', color: '#F47937' }
|
||||||
|
- { label: 'VITEC Orange Light', color: '#FBD2BF' }
|
||||||
|
- { label: 'VITEC Blue', color: '#26358C' }
|
||||||
|
- { label: 'VITEC Blue Light', color: '#C9D0EE' }
|
||||||
|
- { label: 'VITEC Graphite', color: '#313131' }
|
||||||
|
- { label: 'VITEC Midnight', color: '#0D0D0D' }
|
||||||
|
- { label: 'VITEC Light', color: '#CCCCCC' }
|
||||||
|
- { label: 'White', color: '#FFFFFF' }
|
||||||
|
|
||||||
|
# ---- Highlight -----------------------------------------
|
||||||
|
highlight:
|
||||||
|
options:
|
||||||
|
- { model: 'yellowMarker', class: 'marker-yellow', title: 'Yellow marker', color: '#fdfd77', type: 'marker' }
|
||||||
|
- { model: 'greenMarker', class: 'marker-green', title: 'Green marker', color: '#63f963', type: 'marker' }
|
||||||
|
- { model: 'pinkMarker', class: 'marker-pink', title: 'Pink marker', color: '#fc7999', type: 'marker' }
|
||||||
|
- { model: 'blueMarker', class: 'marker-blue', title: 'Blue marker', color: '#72cdfd', type: 'marker' }
|
||||||
|
- { model: 'redPen', class: 'pen-red', title: 'Red pen', color: '#e91313', type: 'pen' }
|
||||||
|
- { model: 'greenPen', class: 'pen-green', title: 'Green pen', color: '#118800', type: 'pen' }
|
||||||
|
|
||||||
|
# ---- Special characters --------------------------------
|
||||||
|
specialCharacters:
|
||||||
|
order: 'alphabet'
|
||||||
|
|
||||||
|
# ---- Word count (optional — remove if not needed) -------
|
||||||
|
wordCount:
|
||||||
|
displayWords: true
|
||||||
|
displayCharacters: true
|
||||||
18
packages/vitec/Configuration/Services.yaml
Normal file
18
packages/vitec/Configuration/Services.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
public: false
|
||||||
|
|
||||||
|
Evomedien\Vitec\:
|
||||||
|
resource: '../Classes/*'
|
||||||
|
exclude: '../Classes/Domain/Model/*'
|
||||||
|
|
||||||
|
# Make our VitecBackendLayoutView the canonical BackendLayoutView.
|
||||||
|
# Any service / controller requesting BackendLayoutView gets our override.
|
||||||
|
Evomedien\Vitec\View\VitecBackendLayoutView:
|
||||||
|
public: true
|
||||||
|
|
||||||
|
TYPO3\CMS\Backend\View\BackendLayoutView:
|
||||||
|
alias: Evomedien\Vitec\View\VitecBackendLayoutView
|
||||||
|
public: true
|
||||||
11
packages/vitec/Configuration/Sets/Vitecset/config.yaml
Normal file
11
packages/vitec/Configuration/Sets/Vitecset/config.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: evomedien/vitecset
|
||||||
|
label: VITEC Set
|
||||||
|
settings:
|
||||||
|
website:
|
||||||
|
background:
|
||||||
|
color: '#386492'
|
||||||
|
dependencies:
|
||||||
|
- typo3/fluid-styled-content
|
||||||
|
- friendsoftypo3/headless
|
||||||
|
- itplusx/headless-container
|
||||||
|
- nb-headless-content-blocks/headless-content-blocks
|
||||||
2
packages/vitec/Configuration/Sets/Vitecset/page.tsconfig
Normal file
2
packages/vitec/Configuration/Sets/Vitecset/page.tsconfig
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Use the custom VITEC CKEditor preset for all RTE fields
|
||||||
|
RTE.default.preset = vitec
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
categories:
|
||||||
|
VITEC:
|
||||||
|
label: 'VITEC Settings'
|
||||||
|
description: 'Settings for VITEC Stuff'
|
||||||
|
seo:
|
||||||
|
label: 'SEO Settings'
|
||||||
|
description: 'SEO related settings'
|
||||||
|
menu:
|
||||||
|
label: 'Menu / Navigation'
|
||||||
|
description: 'Footer & meta menu page selection'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
my.vitec.setting:
|
||||||
|
label: 'My example setting'
|
||||||
|
category: VITEC
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
my.seoRelevantSetting:
|
||||||
|
label: 'My SEO relevant setting'
|
||||||
|
category: seo
|
||||||
|
type: int
|
||||||
|
default: 5
|
||||||
|
|
||||||
|
vitec.debugMode:
|
||||||
|
label: 'Enable Debug Mode'
|
||||||
|
description: 'Enable debug mode for VITEC extensions'
|
||||||
|
category: VITEC
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
menu.footer.pageUids:
|
||||||
|
label: 'Footer menu — page UIDs'
|
||||||
|
description: 'Comma-separated list of page UIDs to display in the footer menu (in order).'
|
||||||
|
category: menu
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
menu.meta.pageUids:
|
||||||
|
label: 'Meta menu — page UIDs'
|
||||||
|
description: 'Comma-separated list of page UIDs to display in the meta menu (e.g. Imprint, Privacy).'
|
||||||
|
category: menu
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
44
packages/vitec/Configuration/Sets/Vitecset/setup.typoscript
Normal file
44
packages/vitec/Configuration/Sets/Vitecset/setup.typoscript
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
config {
|
||||||
|
pageTitleProviders {
|
||||||
|
vitec {
|
||||||
|
provider = Evomedien\Vitec\PageTitle\ProductPageTitleProvider
|
||||||
|
before = record
|
||||||
|
before = seo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Headless: Add products field to vitec_productlist
|
||||||
|
tt_content.list.20.vitec_productlist = USER
|
||||||
|
tt_content.list.20.vitec_productlist {
|
||||||
|
userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
|
||||||
|
}
|
||||||
|
|
||||||
|
# Override the JSON structure for vitec_productlist
|
||||||
|
tt_content.list.fields.content.fields.products = USER
|
||||||
|
tt_content.list.fields.content.fields.products {
|
||||||
|
userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
|
||||||
|
}
|
||||||
|
|
||||||
|
# Headless: Add product field to vitec_productshow
|
||||||
|
tt_content.list.20.vitec_productshow = USER
|
||||||
|
tt_content.list.20.vitec_productshow {
|
||||||
|
userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
|
||||||
|
}
|
||||||
|
|
||||||
|
# Override the JSON structure for vitec_productshow
|
||||||
|
tt_content.list.fields.content.fields.product = USER
|
||||||
|
tt_content.list.fields.content.fields.product {
|
||||||
|
userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include container (layout) rendering definitions
|
||||||
|
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript'
|
||||||
|
|
||||||
|
# Ensure headless's content element JSON definitions take precedence over
|
||||||
|
# fluid_styled_content's HTML definitions. This is loaded at the end of the
|
||||||
|
# Vitecset to guarantee winning load order.
|
||||||
|
@import 'EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript'
|
||||||
|
|
||||||
|
# Include menu (navigation) JSON definitions
|
||||||
|
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript'
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
name: evomedien/vitecappset
|
||||||
|
label: VITEC App Set
|
||||||
|
settings:
|
||||||
|
website:
|
||||||
|
background:
|
||||||
|
color: '#386492'
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
categories:
|
||||||
|
myCategory:
|
||||||
|
label: 'My Category'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
my.example.setting:
|
||||||
|
label: 'My example setting'
|
||||||
|
category: myCategory
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
my.seoRelevantSetting:
|
||||||
|
label: 'My SEO relevant setting'
|
||||||
|
category: seo
|
||||||
|
type: int
|
||||||
|
default: 5
|
||||||
41
packages/vitec/Configuration/TCA/Overrides/pages.php
Executable file
41
packages/vitec/Configuration/TCA/Overrides/pages.php
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_pages.xlf:';
|
||||||
|
|
||||||
|
// Replace the default "Frontend Layout" dropdown on the page record
|
||||||
|
// (Page Properties → Appearance → Frontend Layout) with the VITEC layouts.
|
||||||
|
// Values are integer strings (DB column is INT).
|
||||||
|
$GLOBALS['TCA']['pages']['columns']['layout']['config']['items'] = [
|
||||||
|
['label' => $l . 'layout.0', 'value' => '0'],
|
||||||
|
['label' => $l . 'layout.1', 'value' => '1'],
|
||||||
|
['label' => $l . 'layout.2', 'value' => '2'],
|
||||||
|
['label' => $l . 'layout.3', 'value' => '3'],
|
||||||
|
['label' => $l . 'layout.4', 'value' => '4'],
|
||||||
|
['label' => $l . 'layout.5', 'value' => '5'],
|
||||||
|
['label' => $l . 'layout.6', 'value' => '6'],
|
||||||
|
['label' => $l . 'layout.7', 'value' => '7'],
|
||||||
|
['label' => $l . 'layout.8', 'value' => '8'],
|
||||||
|
['label' => $l . 'layout.9', 'value' => '9'],
|
||||||
|
['label' => $l . 'layout.10', 'value' => '10'],
|
||||||
|
['label' => $l . 'layout.11', 'value' => '11'],
|
||||||
|
['label' => $l . 'layout.12', 'value' => '12'],
|
||||||
|
['label' => $l . 'layout.13', 'value' => '13'],
|
||||||
|
['label' => $l . 'layout.14', 'value' => '14'],
|
||||||
|
['label' => $l . 'layout.15', 'value' => '15'],
|
||||||
|
['label' => $l . 'layout.16', 'value' => '16'],
|
||||||
|
['label' => $l . 'layout.17', 'value' => '17'],
|
||||||
|
['label' => $l . 'layout.18', 'value' => '18'],
|
||||||
|
['label' => $l . 'layout.19', 'value' => '19'],
|
||||||
|
['label' => $l . 'layout.20', 'value' => '20'],
|
||||||
|
];
|
||||||
|
$GLOBALS['TCA']['pages']['columns']['layout']['config']['default'] = '0';
|
||||||
|
|
||||||
|
// Hide the backend_layout / backend_layout_next_level fields from the editor —
|
||||||
|
// they are auto-synced from the frontend layout via SyncBackendLayoutHook.
|
||||||
|
$GLOBALS['TCA']['pages']['columns']['backend_layout']['displayCond'] = 'HIDE_FOR_NON_ADMINS';
|
||||||
|
$GLOBALS['TCA']['pages']['columns']['backend_layout_next_level']['displayCond'] = 'HIDE_FOR_NON_ADMINS';
|
||||||
|
})();
|
||||||
46
packages/vitec/Configuration/TCA/Overrides/sys_category.php
Normal file
46
packages/vitec/Configuration/TCA/Overrides/sys_category.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
// filepath: /usr/home/vitecxxx/public_html/live/extensions/vitec/Configuration/TCA/Overrides/sys_category.php
|
||||||
|
|
||||||
|
defined('TYPO3') || die();
|
||||||
|
|
||||||
|
// Add custom fields to sys_category
|
||||||
|
$customSysCategoryColumns = [
|
||||||
|
'class' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'CSS Class',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'filetype' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Filetype (2nd part of filename)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Type - actual Type, can be different to filename',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add the fields to the TCA
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('sys_category', $customSysCategoryColumns);
|
||||||
|
|
||||||
|
// Add the fields to the "General" tab of sys_category
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
|
||||||
|
'sys_category',
|
||||||
|
'class, filetype, type',
|
||||||
|
'',
|
||||||
|
'after:description'
|
||||||
|
);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
defined('TYPO3') || die();
|
||||||
|
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile('vitec', 'Configuration/TypoScript', 'VITEC');
|
||||||
120
packages/vitec/Configuration/TCA/Overrides/tt_content.php
Normal file
120
packages/vitec/Configuration/TCA/Overrides/tt_content.php
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
||||||
|
|
||||||
|
// Register the Simple Card plugin
|
||||||
|
$simplecardPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Simplecard',
|
||||||
|
'Simple Card'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Single Success Story" plugin
|
||||||
|
$usecaseShowPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Usecaseshow',
|
||||||
|
'Single Success Story'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$usecaseShowPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$usecaseShowPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Usecase.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Show Single Market" plugin
|
||||||
|
$marketShowPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Marketshow',
|
||||||
|
'Show Single Market'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$marketShowPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$marketShowPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Market.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Show Single Solution" plugin
|
||||||
|
$solutionShowPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Solutionshow',
|
||||||
|
'Show Single Solution'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$solutionShowPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$solutionShowPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Solution.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Show Products by Category" plugin
|
||||||
|
$productListPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Productlist',
|
||||||
|
'Show Products by Category'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$productListPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$productListPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Productlist.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Show Single Product" plugin
|
||||||
|
$productShowPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Productshow',
|
||||||
|
'Show Single Product'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$productShowPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$productShowPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Productshow.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the "Shows List of all Success Stories" plugin
|
||||||
|
$usecaseListPluginSignature = \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||||
|
'Vitec',
|
||||||
|
'Usecaselist',
|
||||||
|
'Shows List of all Success Stories'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$usecaseListPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$usecaseListPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Usecaselist.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$simplecardPluginSignature] = 'pi_flexform';
|
||||||
|
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
$simplecardPluginSignature,
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Simplecard.xml'
|
||||||
|
);
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['vitec_datasheets'] = 'recursive,select_key,pages';
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['vitec_datasheets'] = 'pi_flexform';
|
||||||
|
ExtensionManagementUtility::addPiFlexFormValue(
|
||||||
|
'vitec_datasheets',
|
||||||
|
'FILE:EXT:vitec/Configuration/FlexForms/Datasheets.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Change frame_class to allow multiple selections
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['renderType'] = 'selectCheckBox';
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['maxitems'] = 999;
|
||||||
|
|
||||||
|
// Add custom frame_class options for Vitec
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'] = array_merge(
|
||||||
|
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'],
|
||||||
|
[
|
||||||
|
['label' => 'Vitec: Full Width', 'value' => 'vitec-full-width'],
|
||||||
|
['label' => 'Vitec: Centered Container', 'value' => 'vitec-centered'],
|
||||||
|
['label' => 'Vitec: Card Style', 'value' => 'vitec-card'],
|
||||||
|
['label' => 'Vitec: Dark Background', 'value' => 'vitec-dark'],
|
||||||
|
['label' => 'Vitec: Highlight Box', 'value' => 'vitec-highlight'],
|
||||||
|
]
|
||||||
|
);
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use B13\Container\Tca\ContainerConfiguration;
|
||||||
|
use B13\Container\Tca\Registry;
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
/** @var Registry $registry */
|
||||||
|
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||||
|
|
||||||
|
$registry->configureContainer(
|
||||||
|
(new ContainerConfiguration(
|
||||||
|
'vitec_cols_25_25_25_25',
|
||||||
|
$l . 'cols_25_25_25_25.title',
|
||||||
|
$l . 'cols_25_25_25_25.description',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
['name' => $l . 'column.1', 'colPos' => 231],
|
||||||
|
['name' => $l . 'column.2', 'colPos' => 232],
|
||||||
|
['name' => $l . 'column.3', 'colPos' => 233],
|
||||||
|
['name' => $l . 'column.4', 'colPos' => 234],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
))
|
||||||
|
->setIcon('EXT:vitec/Resources/Public/Icons/vitec-cols-25-25-25-25.svg')
|
||||||
|
->setGroup('vitec')
|
||||||
|
->setSaveAndCloseInNewContentElementWizard(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['vitec_cols_25_25_25_25']['showitem'] =
|
||||||
|
'--palette--;;general,
|
||||||
|
header;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.heading,
|
||||||
|
subheader;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.subline,
|
||||||
|
tx_vitec_bg_variant,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||||
|
--palette--;;frames,
|
||||||
|
--palette--;;appearanceLinks,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
|
||||||
|
--palette--;;language,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||||
|
--palette--;;hidden,
|
||||||
|
--palette--;;access';
|
||||||
|
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec.elements.vitec_cols_25_25_25_25 {
|
||||||
|
iconIdentifier = vitec-cols-25-25-25-25
|
||||||
|
title = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_25_25_25_25.title
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_25_25_25_25.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = vitec_cols_25_25_25_25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use B13\Container\Tca\ContainerConfiguration;
|
||||||
|
use B13\Container\Tca\Registry;
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
/** @var Registry $registry */
|
||||||
|
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||||
|
|
||||||
|
$registry->configureContainer(
|
||||||
|
(new ContainerConfiguration(
|
||||||
|
'vitec_cols_33_33_33',
|
||||||
|
$l . 'cols_33_33_33.title',
|
||||||
|
$l . 'cols_33_33_33.description',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
['name' => $l . 'column.1', 'colPos' => 221],
|
||||||
|
['name' => $l . 'column.2', 'colPos' => 222],
|
||||||
|
['name' => $l . 'column.3', 'colPos' => 223],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
))
|
||||||
|
->setIcon('EXT:vitec/Resources/Public/Icons/vitec-cols-33-33-33.svg')
|
||||||
|
->setGroup('vitec')
|
||||||
|
->setSaveAndCloseInNewContentElementWizard(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['vitec_cols_33_33_33']['showitem'] =
|
||||||
|
'--palette--;;general,
|
||||||
|
header;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.heading,
|
||||||
|
subheader;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.subline,
|
||||||
|
tx_vitec_bg_variant,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||||
|
--palette--;;frames,
|
||||||
|
--palette--;;appearanceLinks,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
|
||||||
|
--palette--;;language,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||||
|
--palette--;;hidden,
|
||||||
|
--palette--;;access';
|
||||||
|
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec.elements.vitec_cols_33_33_33 {
|
||||||
|
iconIdentifier = vitec-cols-33-33-33
|
||||||
|
title = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_33_33_33.title
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_33_33_33.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = vitec_cols_33_33_33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_66.php
Executable file
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_66.php
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use B13\Container\Tca\ContainerConfiguration;
|
||||||
|
use B13\Container\Tca\Registry;
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
/** @var Registry $registry */
|
||||||
|
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||||
|
|
||||||
|
$registry->configureContainer(
|
||||||
|
(new ContainerConfiguration(
|
||||||
|
'vitec_cols_33_66',
|
||||||
|
$l . 'cols_33_66.title',
|
||||||
|
$l . 'cols_33_66.description',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
['name' => $l . 'column.sidebar_33', 'colPos' => 251],
|
||||||
|
['name' => $l . 'column.main_66', 'colPos' => 252],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
))
|
||||||
|
->setIcon('EXT:vitec/Resources/Public/Icons/vitec-cols-33-66.svg')
|
||||||
|
->setGroup('vitec')
|
||||||
|
->setSaveAndCloseInNewContentElementWizard(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['vitec_cols_33_66']['showitem'] =
|
||||||
|
'--palette--;;general,
|
||||||
|
header;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.heading,
|
||||||
|
subheader;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.subline,
|
||||||
|
tx_vitec_bg_variant,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||||
|
--palette--;;frames,
|
||||||
|
--palette--;;appearanceLinks,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
|
||||||
|
--palette--;;language,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||||
|
--palette--;;hidden,
|
||||||
|
--palette--;;access';
|
||||||
|
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec.elements.vitec_cols_33_66 {
|
||||||
|
iconIdentifier = vitec-cols-33-66
|
||||||
|
title = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_33_66.title
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_33_66.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = vitec_cols_33_66
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_50_50.php
Executable file
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_50_50.php
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use B13\Container\Tca\ContainerConfiguration;
|
||||||
|
use B13\Container\Tca\Registry;
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
/** @var Registry $registry */
|
||||||
|
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||||
|
|
||||||
|
$registry->configureContainer(
|
||||||
|
(new ContainerConfiguration(
|
||||||
|
'vitec_cols_50_50',
|
||||||
|
$l . 'cols_50_50.title',
|
||||||
|
$l . 'cols_50_50.description',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
['name' => $l . 'column.1', 'colPos' => 211],
|
||||||
|
['name' => $l . 'column.2', 'colPos' => 212],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
))
|
||||||
|
->setIcon('EXT:vitec/Resources/Public/Icons/vitec-cols-50-50.svg')
|
||||||
|
->setGroup('vitec')
|
||||||
|
->setSaveAndCloseInNewContentElementWizard(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['vitec_cols_50_50']['showitem'] =
|
||||||
|
'--palette--;;general,
|
||||||
|
header;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.heading,
|
||||||
|
subheader;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.subline,
|
||||||
|
tx_vitec_bg_variant,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||||
|
--palette--;;frames,
|
||||||
|
--palette--;;appearanceLinks,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
|
||||||
|
--palette--;;language,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||||
|
--palette--;;hidden,
|
||||||
|
--palette--;;access';
|
||||||
|
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec.elements.vitec_cols_50_50 {
|
||||||
|
iconIdentifier = vitec-cols-50-50
|
||||||
|
title = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_50_50.title
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_50_50.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = vitec_cols_50_50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_66_33.php
Executable file
58
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_66_33.php
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use B13\Container\Tca\ContainerConfiguration;
|
||||||
|
use B13\Container\Tca\Registry;
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
/** @var Registry $registry */
|
||||||
|
$registry = GeneralUtility::makeInstance(Registry::class);
|
||||||
|
|
||||||
|
$registry->configureContainer(
|
||||||
|
(new ContainerConfiguration(
|
||||||
|
'vitec_cols_66_33',
|
||||||
|
$l . 'cols_66_33.title',
|
||||||
|
$l . 'cols_66_33.description',
|
||||||
|
[
|
||||||
|
[
|
||||||
|
['name' => $l . 'column.main_66', 'colPos' => 241],
|
||||||
|
['name' => $l . 'column.sidebar_33', 'colPos' => 242],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
))
|
||||||
|
->setIcon('EXT:vitec/Resources/Public/Icons/vitec-cols-66-33.svg')
|
||||||
|
->setGroup('vitec')
|
||||||
|
->setSaveAndCloseInNewContentElementWizard(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
$GLOBALS['TCA']['tt_content']['types']['vitec_cols_66_33']['showitem'] =
|
||||||
|
'--palette--;;general,
|
||||||
|
header;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.heading,
|
||||||
|
subheader;LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:section.subline,
|
||||||
|
tx_vitec_bg_variant,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||||
|
--palette--;;frames,
|
||||||
|
--palette--;;appearanceLinks,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
|
||||||
|
--palette--;;language,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||||
|
--palette--;;hidden,
|
||||||
|
--palette--;;access';
|
||||||
|
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec.elements.vitec_cols_66_33 {
|
||||||
|
iconIdentifier = vitec-cols-66-33
|
||||||
|
title = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_66_33.title
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:cols_66_33.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = vitec_cols_66_33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
37
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_shared.php
Executable file
37
packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_shared.php
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('TYPO3') or die();
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||||
|
|
||||||
|
(static function (): void {
|
||||||
|
$l = 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:';
|
||||||
|
|
||||||
|
// Shared field: Background Variant for all VITEC containers
|
||||||
|
ExtensionManagementUtility::addTCAcolumns('tt_content', [
|
||||||
|
'tx_vitec_bg_variant' => [
|
||||||
|
'label' => $l . 'bg_variant.label',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'items' => [
|
||||||
|
['label' => $l . 'bg_variant.option.none', 'value' => 'none'],
|
||||||
|
['label' => $l . 'bg_variant.option.orange', 'value' => 'orange'],
|
||||||
|
['label' => $l . 'bg_variant.option.blue', 'value' => 'blue'],
|
||||||
|
['label' => $l . 'bg_variant.option.graphite', 'value' => 'graphite'],
|
||||||
|
['label' => $l . 'bg_variant.option.midnight', 'value' => 'midnight'],
|
||||||
|
],
|
||||||
|
'default' => 'none',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Register VITEC wizard group header
|
||||||
|
ExtensionManagementUtility::addPageTSConfig(<<<TSCONFIG
|
||||||
|
mod.wizards.newContentElement.wizardItems.vitec {
|
||||||
|
header = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:group.header
|
||||||
|
show = *
|
||||||
|
}
|
||||||
|
TSCONFIG);
|
||||||
|
})();
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ctrl' => [
|
||||||
|
'title' => 'VITEC Download',
|
||||||
|
'label' => 'title',
|
||||||
|
'tstamp' => 'tstamp',
|
||||||
|
'crdate' => 'crdate',
|
||||||
|
'cruser_id' => 'cruser_id',
|
||||||
|
'versioningWS' => true,
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
'transOrigPointerField' => 'l10n_parent',
|
||||||
|
'transOrigDiffSourceField' => 'l10n_diffsource',
|
||||||
|
'delete' => 'deleted',
|
||||||
|
'enablecolumns' => [
|
||||||
|
'disabled' => 'hidden',
|
||||||
|
'starttime' => 'starttime',
|
||||||
|
'endtime' => 'endtime',
|
||||||
|
],
|
||||||
|
'searchFields' => 'title,slug,teaser,description,sort1,sort2,sort3,icon,filepath,fileprefix',
|
||||||
|
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_download.gif'
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'1' => ['showitem' => 'hidden, title, slug, keywords, teaser, description,
|
||||||
|
--div--;File, useolddl, file, fileprefix, categories,
|
||||||
|
--div--;Visibility, hideonapp, hideonwebsite, hideondatasheets, hideonproducts,
|
||||||
|
--div--;Currently not in use,icon, filepath, private_download, onlyondatasheets, sort1, sort2, sort3,
|
||||||
|
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, starttime, endtime'],
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'special' => 'languages',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages',
|
||||||
|
-1,
|
||||||
|
'flags-multiple'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_parent' => [
|
||||||
|
'displayCond' => 'FIELD:sys_language_uid:>:0',
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
['', 0],
|
||||||
|
],
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_download',
|
||||||
|
'foreign_table_where' => 'AND {#tx_vitec_domain_model_download}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_download}.{#sys_language_uid} IN (-1,0)',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_diffsource' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'passthrough',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hidden' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'starttime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'range' => [
|
||||||
|
'upper' => mktime(0, 0, 0, 1, 1, 2038)
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'categories' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'category'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Title',
|
||||||
|
'description' => 'Title of the Download - this will be shown in search results',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'teaser' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Teaser Text for Download Cards',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'slug' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Slug',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'slug',
|
||||||
|
'generatorOptions' => [
|
||||||
|
'fields' => ['title'],
|
||||||
|
'fieldSeparator' => '/',
|
||||||
|
'prefixParentPageSlug' => true,
|
||||||
|
'replacements' => [
|
||||||
|
'/' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'fallbackCharacter' => '-',
|
||||||
|
'eval' => 'unique',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'keywords' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Keywords for internal search engine',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => true,
|
||||||
|
'richtextConfiguration' => 'default',
|
||||||
|
'fieldControl' => [
|
||||||
|
'fullScreenRichtext' => [
|
||||||
|
'disabled' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'cols' => 40,
|
||||||
|
'rows' => 15,
|
||||||
|
'eval' => 'trim',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
'file' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Manual File Upload - if "Use manual file upload" is checked',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'file',
|
||||||
|
'appearance' => [
|
||||||
|
'createNewRelationLinkTitle' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:media.addFileReference'
|
||||||
|
],
|
||||||
|
'foreign_types' => [
|
||||||
|
'0' => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
],
|
||||||
|
\TYPO3\CMS\Core\Resource\File::FILETYPE_TEXT => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
],
|
||||||
|
\TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
],
|
||||||
|
\TYPO3\CMS\Core\Resource\File::FILETYPE_AUDIO => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
],
|
||||||
|
\TYPO3\CMS\Core\Resource\File::FILETYPE_VIDEO => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
],
|
||||||
|
\TYPO3\CMS\Core\Resource\File::FILETYPE_APPLICATION => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;LLL:EXT:lang/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'file',
|
||||||
|
'tablenames' => 'tx_vitec_domain_model_download',
|
||||||
|
],
|
||||||
|
'maxitems' => 1
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'sort1' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.sort1',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'sort2' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.sort2',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'sort3' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.sort3',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'private_download' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.private_download',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'hideonapp' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Do not show this Download in the App',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'hideonwebsite' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Do not show this Download on the Website',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'hideondatasheets' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Do not show this Download on the Datasheets-Page',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'hideonproducts' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Do not show this Download on a Products-Page',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'icon' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.icon',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'filepath' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.filepath',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'fileprefix' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Fileprefix (1st part of filename)',
|
||||||
|
'description' => 'This is the first part of the filename.',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'useolddl' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Use manual file upload',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'default' => 0,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ctrl' => [
|
||||||
|
'title' => 'VITEC Market',
|
||||||
|
'label' => 'title',
|
||||||
|
'tstamp' => 'tstamp',
|
||||||
|
'crdate' => 'crdate',
|
||||||
|
'cruser_id' => 'cruser_id',
|
||||||
|
'versioningWS' => true,
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
'transOrigPointerField' => 'l10n_parent',
|
||||||
|
'transOrigDiffSourceField' => 'l10n_diffsource',
|
||||||
|
'delete' => 'deleted',
|
||||||
|
'enablecolumns' => [
|
||||||
|
'disabled' => 'hidden',
|
||||||
|
'starttime' => 'starttime',
|
||||||
|
'endtime' => 'endtime',
|
||||||
|
],
|
||||||
|
'searchFields' => 'title,subtitle,teaser',
|
||||||
|
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_market.gif',
|
||||||
|
'security' => [
|
||||||
|
'ignorePageTypeRestriction' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'1' => [
|
||||||
|
'showitem' => 'title, subtitle, teaser, description, image,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'language',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_parent' => [
|
||||||
|
'displayCond' => 'FIELD:sys_language_uid:>:0',
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
['', 0],
|
||||||
|
],
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_market',
|
||||||
|
'foreign_table_where' => 'AND {#tx_vitec_domain_model_market}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_market}.{#sys_language_uid} IN (-1,0)',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_diffsource' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'passthrough',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hidden' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'starttime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'range' => [
|
||||||
|
'upper' => mktime(0, 0, 0, 1, 1, 2038),
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'Title',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'required' => true,
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'subtitle' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Subtitle',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'teaser' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Short Teaser (optional)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => true,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]',
|
||||||
|
],
|
||||||
|
'categories' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'category',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'image' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Image',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'image',
|
||||||
|
],
|
||||||
|
'foreign_label' => 'uid_local',
|
||||||
|
'foreign_selector' => 'uid_local',
|
||||||
|
'overrideChildTca' => [
|
||||||
|
'columns' => [
|
||||||
|
'uid_local' => [
|
||||||
|
'config' => [
|
||||||
|
'appearance' => [
|
||||||
|
'elementBrowserType' => 'file',
|
||||||
|
'elementBrowserAllowed' => 'jpg,jpeg,png,gif,webp,svg',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'0' => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 1,
|
||||||
|
'appearance' => [
|
||||||
|
'headerThumbnail' => [
|
||||||
|
'field' => 'uid_local',
|
||||||
|
'width' => '45',
|
||||||
|
'height' => '45',
|
||||||
|
],
|
||||||
|
'enabledControls' => [
|
||||||
|
'info' => true,
|
||||||
|
'new' => false,
|
||||||
|
'dragdrop' => true,
|
||||||
|
'sort' => false,
|
||||||
|
'hide' => true,
|
||||||
|
'delete' => true,
|
||||||
|
],
|
||||||
|
'fileUploadAllowed' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,541 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ctrl' => [
|
||||||
|
'title' => 'VITEC Product',
|
||||||
|
'label' => 'title',
|
||||||
|
'tstamp' => 'tstamp',
|
||||||
|
'crdate' => 'crdate',
|
||||||
|
'cruser_id' => 'cruser_id',
|
||||||
|
'versioningWS' => true,
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
'transOrigPointerField' => 'l10n_parent',
|
||||||
|
'transOrigDiffSourceField' => 'l10n_diffsource',
|
||||||
|
'delete' => 'deleted',
|
||||||
|
'enablecolumns' => [
|
||||||
|
'disabled' => 'hidden',
|
||||||
|
'starttime' => 'starttime',
|
||||||
|
'endtime' => 'endtime',
|
||||||
|
],
|
||||||
|
'searchFields' => 'title,slug',
|
||||||
|
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_product.gif',
|
||||||
|
'security' => [
|
||||||
|
'ignorePageTypeRestriction' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'1' => ['showitem' => 'title, subtitle, slug, teaser, description, applications, highlights, contentelement, contentelementcta, downloads,
|
||||||
|
--div--;SEO, seotitle, urltitle, seometa, keywords, structureddata,
|
||||||
|
--div--;Images and Videos, productimage, ogimage, video,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories,
|
||||||
|
--div--;Visibility, hideonapp, hideonwebsite, hideondatasheets, hideonproducts, shortcut, shortcutpid,
|
||||||
|
--div--;Misc, legacy, supportproduct, subproduct, relatedprodukt,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime'],
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'language',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_parent' => [
|
||||||
|
'displayCond' => 'FIELD:sys_language_uid:>:0',
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
['', 0],
|
||||||
|
],
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_product',
|
||||||
|
'foreign_table_where' => 'AND {#tx_vitec_domain_model_product}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_product}.{#sys_language_uid} IN (-1,0)',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_diffsource' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'passthrough',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hidden' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'starttime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'range' => [
|
||||||
|
'upper' => mktime(0, 0, 0, 1, 1, 2038)
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'categories' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'category'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'required' => true,
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'slug' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'slug',
|
||||||
|
'size' => 50,
|
||||||
|
'generatorOptions' => [
|
||||||
|
'fields' => ['title'], // TODO: adjust this field to the one you want to use
|
||||||
|
'fieldSeparator' => '-',
|
||||||
|
'replacements' => [
|
||||||
|
'/' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'fallbackCharacter' => '-',
|
||||||
|
'eval' => 'uniqueInPid',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
'seotitle' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'SEO-Title',
|
||||||
|
'description' => 'SEO specific title, used for sharing options',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'urltitle' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'URL-Title',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.urltitle.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'video' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'Product Video - just the Youtube Clip ID',
|
||||||
|
'description' => 'Product Video - just the Youtube Clip ID',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'downloads' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Downloads',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectMultipleSideBySide',
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_download',
|
||||||
|
'MM' => 'tx_vitec_product_download_mm', // Define the MM table for the relation
|
||||||
|
'size' => 10,
|
||||||
|
'maxitems' => 9999,
|
||||||
|
'minitems' => 0,
|
||||||
|
'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'structureddata' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Structured Data in JSON Format',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'cols' => 40,
|
||||||
|
'rows' => 15,
|
||||||
|
'eval' => 'trim'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'keywords' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Keywords',
|
||||||
|
'description' => 'Keywords for SEO, separated by commas',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'cols' => 40,
|
||||||
|
'rows' => 15,
|
||||||
|
'eval' => 'trim'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'seometa' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'SEO Meta Description',
|
||||||
|
'description' => 'SEO specific meta description, used for sharing options',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'cols' => 40,
|
||||||
|
'rows' => 15,
|
||||||
|
'eval' => 'trim'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'teaser' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Short Teaser (optional)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'subtitle' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Subtitle',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hideonapp' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on App',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hideonwebsite' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on Website',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'hideonproducts' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on Product Section',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'hideondatasheets' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on Datasheets Page',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'applications' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Applications',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => 'true',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]'
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => 'true',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]'
|
||||||
|
],
|
||||||
|
|
||||||
|
'highlights' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Highlights',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => 'true',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]'
|
||||||
|
],
|
||||||
|
|
||||||
|
'shortcut' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Does this Product have a custom Page',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'shortcutpid' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'Page ID',
|
||||||
|
'description' => 'PID of the custom page',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => ''
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'legacy' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Is this a legacy produc?',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'supportproduct' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Is this a support product?',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'subproduct' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Product is a sub-products',
|
||||||
|
'description' => 'This Product will not be shown as a main product',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'relatedprodukt' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Related Products',
|
||||||
|
'description' => 'Select related products for this product',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectMultipleSideBySide',
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_product', // Reference the same table
|
||||||
|
'MM' => 'tx_vitec_product_related_mm', // Define the MM table for the relation
|
||||||
|
'size' => 10,
|
||||||
|
'maxitems' => 9999,
|
||||||
|
'minitems' => 0,
|
||||||
|
'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'productimage' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Product Image(s)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'productimage',
|
||||||
|
],
|
||||||
|
'appearance' => [
|
||||||
|
'collapseAll' => true,
|
||||||
|
'levelLinksPosition' => 'top',
|
||||||
|
'showSynchronizationLink' => true,
|
||||||
|
'showPossibleLocalizationRecords' => true,
|
||||||
|
'showAllLocalizationLink' => true,
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
'filter' => [
|
||||||
|
[
|
||||||
|
'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren',
|
||||||
|
'parameters' => [
|
||||||
|
'allowedFileExtensions' => 'jpg,jpeg,png,gif',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 10,
|
||||||
|
'minitems' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'ogimage' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Open Graph Image',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'ogimage',
|
||||||
|
],
|
||||||
|
'appearance' => [
|
||||||
|
'collapseAll' => true,
|
||||||
|
'levelLinksPosition' => 'top',
|
||||||
|
'showSynchronizationLink' => true,
|
||||||
|
'showPossibleLocalizationRecords' => true,
|
||||||
|
'showAllLocalizationLink' => true,
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
'filter' => [
|
||||||
|
[
|
||||||
|
'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren',
|
||||||
|
'parameters' => [
|
||||||
|
'allowedFileExtensions' => 'jpg,jpeg,png,gif,webp',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 1, // Allow only one image
|
||||||
|
'minitems' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'contentelement' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Select Content Element for Key Features Section',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputLink',
|
||||||
|
'softref' => 'typolink',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'fieldControl' => [
|
||||||
|
'linkPopup' => [
|
||||||
|
'options' => [
|
||||||
|
'title' => 'Select Content Element',
|
||||||
|
'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types
|
||||||
|
'blindLinkFields' => 'class,params', // Disable unnecessary fields
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'contentelementcta' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Select Content Element for CTA Section',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputLink',
|
||||||
|
'softref' => 'typolink',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'fieldControl' => [
|
||||||
|
'linkPopup' => [
|
||||||
|
'options' => [
|
||||||
|
'title' => 'Select Content Element',
|
||||||
|
'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types
|
||||||
|
'blindLinkFields' => 'class,params', // Disable unnecessary fields
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/* ----------------------------------------------------- */
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ctrl' => [
|
||||||
|
'title' => 'VITEC Solution',
|
||||||
|
'label' => 'title',
|
||||||
|
'tstamp' => 'tstamp',
|
||||||
|
'crdate' => 'crdate',
|
||||||
|
'cruser_id' => 'cruser_id',
|
||||||
|
'versioningWS' => true,
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
'transOrigPointerField' => 'l10n_parent',
|
||||||
|
'transOrigDiffSourceField' => 'l10n_diffsource',
|
||||||
|
'delete' => 'deleted',
|
||||||
|
'enablecolumns' => [
|
||||||
|
'disabled' => 'hidden',
|
||||||
|
'starttime' => 'starttime',
|
||||||
|
'endtime' => 'endtime',
|
||||||
|
],
|
||||||
|
'searchFields' => 'title,subtitle,teaser',
|
||||||
|
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_solution.gif',
|
||||||
|
'security' => [
|
||||||
|
'ignorePageTypeRestriction' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'1' => [
|
||||||
|
'showitem' => 'title, subtitle, teaser, description, image,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'language',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_parent' => [
|
||||||
|
'displayCond' => 'FIELD:sys_language_uid:>:0',
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
['', 0],
|
||||||
|
],
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_solution',
|
||||||
|
'foreign_table_where' => 'AND {#tx_vitec_domain_model_solution}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_solution}.{#sys_language_uid} IN (-1,0)',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_diffsource' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'passthrough',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hidden' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'starttime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'range' => [
|
||||||
|
'upper' => mktime(0, 0, 0, 1, 1, 2038),
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'Title',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'required' => true,
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'subtitle' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Subtitle',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'teaser' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Short Teaser (optional)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => true,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]',
|
||||||
|
],
|
||||||
|
'categories' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'category',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'image' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Image',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'image',
|
||||||
|
],
|
||||||
|
'foreign_label' => 'uid_local',
|
||||||
|
'foreign_selector' => 'uid_local',
|
||||||
|
'overrideChildTca' => [
|
||||||
|
'columns' => [
|
||||||
|
'uid_local' => [
|
||||||
|
'config' => [
|
||||||
|
'appearance' => [
|
||||||
|
'elementBrowserType' => 'file',
|
||||||
|
'elementBrowserAllowed' => 'jpg,jpeg,png,gif,webp,svg',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'0' => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 1,
|
||||||
|
'appearance' => [
|
||||||
|
'headerThumbnail' => [
|
||||||
|
'field' => 'uid_local',
|
||||||
|
'width' => '45',
|
||||||
|
'height' => '45',
|
||||||
|
],
|
||||||
|
'enabledControls' => [
|
||||||
|
'info' => true,
|
||||||
|
'new' => false,
|
||||||
|
'dragdrop' => true,
|
||||||
|
'sort' => false,
|
||||||
|
'hide' => true,
|
||||||
|
'delete' => true,
|
||||||
|
],
|
||||||
|
'fileUploadAllowed' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ctrl' => [
|
||||||
|
'title' => 'VITEC Success Story',
|
||||||
|
'label' => 'title',
|
||||||
|
'tstamp' => 'tstamp',
|
||||||
|
'crdate' => 'crdate',
|
||||||
|
'cruser_id' => 'cruser_id',
|
||||||
|
'versioningWS' => true,
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
'transOrigPointerField' => 'l10n_parent',
|
||||||
|
'transOrigDiffSourceField' => 'l10n_diffsource',
|
||||||
|
'delete' => 'deleted',
|
||||||
|
'enablecolumns' => [
|
||||||
|
'disabled' => 'hidden',
|
||||||
|
'starttime' => 'starttime',
|
||||||
|
'endtime' => 'endtime',
|
||||||
|
],
|
||||||
|
'searchFields' => 'title,slug',
|
||||||
|
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_usecase.gif',
|
||||||
|
'security' => [
|
||||||
|
'ignorePageTypeRestriction' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'1' => ['showitem' => 'title, slug, subtitle, teaser, description, singlepid, caseimage, logoimage,
|
||||||
|
--div--;Visibility, hideonapp, hideonwebsite,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories,
|
||||||
|
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime', ],
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'language',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_parent' => [
|
||||||
|
'displayCond' => 'FIELD:sys_language_uid:>:0',
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'select',
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
['', 0],
|
||||||
|
],
|
||||||
|
'foreign_table' => 'tx_vitec_domain_model_download',
|
||||||
|
'foreign_table_where' => 'AND {#tx_vitec_domain_model_download}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_download}.{#sys_language_uid} IN (-1,0)',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'l10n_diffsource' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'passthrough',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hidden' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'starttime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'default' => 0,
|
||||||
|
'range' => [
|
||||||
|
'upper' => mktime(0, 0, 0, 1, 1, 2038),
|
||||||
|
],
|
||||||
|
'behaviour' => [
|
||||||
|
'allowLanguageSynchronization' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'title' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.title',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.title.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'required' => true,
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'slug' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.slug',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.slug.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'slug',
|
||||||
|
'size' => 50,
|
||||||
|
'generatorOptions' => [
|
||||||
|
'fields' => ['title'], // TODO: adjust this field to the one you want to use
|
||||||
|
'fieldSeparator' => '-',
|
||||||
|
'replacements' => [
|
||||||
|
'/' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'fallbackCharacter' => '-',
|
||||||
|
'eval' => 'uniqueInPid',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
'singlepid' => [
|
||||||
|
'exclude' => false,
|
||||||
|
'label' => 'Page ID of Success Story',
|
||||||
|
'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_download.title.description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'teaser' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Short Teaser (optional)',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'subtitle' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Subtitle',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'size' => 30,
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hideonapp' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on App',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'hideonwebsite' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'If checked - Product will not be shown on Website',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'check',
|
||||||
|
'renderType' => 'checkboxToggle',
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
0 => '',
|
||||||
|
1 => '',
|
||||||
|
'invertStateDisplay' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Description',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'enableRichtext' => 'true',
|
||||||
|
'eval' => 'trim',
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
'defaultExtras' => 'richtext:rte_transform[mode=ts_css]',
|
||||||
|
],
|
||||||
|
'caseimage' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Case Image',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'caseimage',
|
||||||
|
],
|
||||||
|
'foreign_label' => 'uid_local',
|
||||||
|
'foreign_selector' => 'uid_local',
|
||||||
|
'overrideChildTca' => [
|
||||||
|
'columns' => [
|
||||||
|
'uid_local' => [
|
||||||
|
'config' => [
|
||||||
|
'appearance' => [
|
||||||
|
'elementBrowserType' => 'file',
|
||||||
|
'elementBrowserAllowed' => 'jpg,jpeg,png,gif',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'0' => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 1,
|
||||||
|
'appearance' => [
|
||||||
|
'headerThumbnail' => [
|
||||||
|
'field' => 'uid_local',
|
||||||
|
'width' => '45',
|
||||||
|
'height' => '45',
|
||||||
|
],
|
||||||
|
'enabledControls' => [
|
||||||
|
'info' => true,
|
||||||
|
'new' => false,
|
||||||
|
'dragdrop' => true,
|
||||||
|
'sort' => false,
|
||||||
|
'hide' => true,
|
||||||
|
'delete' => true,
|
||||||
|
],
|
||||||
|
'fileUploadAllowed' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'logoimage' => [
|
||||||
|
'exclude' => true,
|
||||||
|
'label' => 'Customer Logo',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'inline',
|
||||||
|
'foreign_table' => 'sys_file_reference',
|
||||||
|
'foreign_field' => 'uid_foreign',
|
||||||
|
'foreign_sortby' => 'sorting_foreign',
|
||||||
|
'foreign_table_field' => 'tablenames',
|
||||||
|
'foreign_match_fields' => [
|
||||||
|
'fieldname' => 'logoimage',
|
||||||
|
],
|
||||||
|
'foreign_label' => 'uid_local',
|
||||||
|
'foreign_selector' => 'uid_local',
|
||||||
|
'overrideChildTca' => [
|
||||||
|
'columns' => [
|
||||||
|
'uid_local' => [
|
||||||
|
'config' => [
|
||||||
|
'appearance' => [
|
||||||
|
'elementBrowserType' => 'file',
|
||||||
|
'elementBrowserAllowed' => 'jpg,jpeg,png,gif',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'types' => [
|
||||||
|
'0' => [
|
||||||
|
'showitem' => '
|
||||||
|
--palette--;;imageoverlayPalette,
|
||||||
|
--palette--;;filePalette',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'maxitems' => 1,
|
||||||
|
'appearance' => [
|
||||||
|
'headerThumbnail' => [
|
||||||
|
'field' => 'uid_local',
|
||||||
|
'width' => '45',
|
||||||
|
'height' => '45',
|
||||||
|
],
|
||||||
|
'enabledControls' => [
|
||||||
|
'info' => true,
|
||||||
|
'new' => false,
|
||||||
|
'dragdrop' => true,
|
||||||
|
'sort' => false,
|
||||||
|
'hide' => true,
|
||||||
|
'delete' => true,
|
||||||
|
],
|
||||||
|
'fileUploadAllowed' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'categories' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'category'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
89
packages/vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript
Executable file
89
packages/vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# VITEC container content elements — self-contained JSON rendering
|
||||||
|
# Children collected via Evomedien\Vitec\DataProcessing\ContainerChildrenProcessor
|
||||||
|
# (exception-safe; returns [] on any error, never crashes the outer CE).
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
tt_content.vitec_cols_50_50 = JSON
|
||||||
|
tt_content.vitec_cols_50_50 {
|
||||||
|
fields {
|
||||||
|
id = INT
|
||||||
|
id.field = uid
|
||||||
|
type = TEXT
|
||||||
|
type.field = CType
|
||||||
|
colPos = INT
|
||||||
|
colPos.field = colPos
|
||||||
|
|
||||||
|
categories = COA
|
||||||
|
categories {
|
||||||
|
10 = CONTENT
|
||||||
|
10 {
|
||||||
|
table = sys_category
|
||||||
|
select {
|
||||||
|
pidInList = root
|
||||||
|
selectFields = sys_category.title
|
||||||
|
join = sys_category_record_mm on sys_category_record_mm.uid_local = sys_category.uid
|
||||||
|
where {
|
||||||
|
field = uid
|
||||||
|
wrap = AND sys_category_record_mm.tablenames = 'tt_content' AND sys_category_record_mm.uid_foreign=|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderObj = TEXT
|
||||||
|
renderObj {
|
||||||
|
field = title
|
||||||
|
wrap = |###BREAK###
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdWrap.split {
|
||||||
|
token = ###BREAK###
|
||||||
|
cObjNum = 1 |*|2|*| 3
|
||||||
|
1 {
|
||||||
|
current = 1
|
||||||
|
stdWrap.wrap = |
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
current = 1
|
||||||
|
stdWrap.wrap = ,|
|
||||||
|
}
|
||||||
|
3 {
|
||||||
|
current = 1
|
||||||
|
stdWrap.wrap = |
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appearance = JSON
|
||||||
|
appearance {
|
||||||
|
fields {
|
||||||
|
layout = TEXT
|
||||||
|
layout.field = layout
|
||||||
|
frameClass = TEXT
|
||||||
|
frameClass.field = frame_class
|
||||||
|
spaceBefore = TEXT
|
||||||
|
spaceBefore.field = space_before_class
|
||||||
|
spaceAfter = TEXT
|
||||||
|
spaceAfter.field = space_after_class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header = TEXT
|
||||||
|
header.field = header
|
||||||
|
subheader = TEXT
|
||||||
|
subheader.field = subheader
|
||||||
|
tx_vitec_bg_variant = TEXT
|
||||||
|
tx_vitec_bg_variant.field = tx_vitec_bg_variant
|
||||||
|
|
||||||
|
items = JSON
|
||||||
|
items {
|
||||||
|
dataProcessing {
|
||||||
|
10 = Evomedien\Vitec\DataProcessing\ContainerChildrenProcessor
|
||||||
|
10.as = items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tt_content.vitec_cols_33_33_33 =< tt_content.vitec_cols_50_50
|
||||||
|
tt_content.vitec_cols_25_25_25_25 =< tt_content.vitec_cols_50_50
|
||||||
|
tt_content.vitec_cols_66_33 =< tt_content.vitec_cols_50_50
|
||||||
|
tt_content.vitec_cols_33_66 =< tt_content.vitec_cols_50_50
|
||||||
74
packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript
Executable file
74
packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# VITEC menus — Headless / JSON output
|
||||||
|
#
|
||||||
|
# Three menus are exposed at the page root of the JSON response:
|
||||||
|
# - mainNavigation : full hierarchy from root, all visible pages
|
||||||
|
# - footerMenu : curated, configured via site setting menu.footer.pageUids
|
||||||
|
# - metaMenu : curated, configured via site setting menu.meta.pageUids
|
||||||
|
#
|
||||||
|
# Each menu uses friendsoftypo3/headless MenuProcessor which:
|
||||||
|
# * resolves Shortcut pages to their target
|
||||||
|
# * skips pages with "Show in menu = no" (nav_hide=1) by default
|
||||||
|
# * marks active / current items
|
||||||
|
# * exposes `children` for sub-levels
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Main navigation — full hierarchy
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
lib.mainNavigation = JSON
|
||||||
|
lib.mainNavigation {
|
||||||
|
dataProcessing {
|
||||||
|
10 = FriendsOfTYPO3\Headless\DataProcessing\MenuProcessor
|
||||||
|
10 {
|
||||||
|
levels = 10
|
||||||
|
expandAll = 1
|
||||||
|
includeSpacer = 0
|
||||||
|
titleField = nav_title // title
|
||||||
|
as = mainNavigation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Footer menu — curated by site setting (UIDs)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
lib.footerMenu = JSON
|
||||||
|
lib.footerMenu {
|
||||||
|
dataProcessing {
|
||||||
|
10 = FriendsOfTYPO3\Headless\DataProcessing\MenuProcessor
|
||||||
|
10 {
|
||||||
|
special = list
|
||||||
|
special.value = {$menu.footer.pageUids}
|
||||||
|
levels = 1
|
||||||
|
includeSpacer = 0
|
||||||
|
titleField = nav_title // title
|
||||||
|
as = footerMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Meta menu — curated by site setting (UIDs)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
lib.metaMenu = JSON
|
||||||
|
lib.metaMenu {
|
||||||
|
dataProcessing {
|
||||||
|
10 = FriendsOfTYPO3\Headless\DataProcessing\MenuProcessor
|
||||||
|
10 {
|
||||||
|
special = list
|
||||||
|
special.value = {$menu.meta.pageUids}
|
||||||
|
levels = 1
|
||||||
|
includeSpacer = 0
|
||||||
|
titleField = nav_title // title
|
||||||
|
as = metaMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Wire them into the page-level JSON output
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
page.10.fields.mainNavigation =< lib.mainNavigation
|
||||||
|
page.10.fields.footerMenu =< lib.footerMenu
|
||||||
|
page.10.fields.metaMenu =< lib.metaMenu
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Test if we can add any field at all
|
||||||
|
tt_content.list.fields.content.fields.testField = TEXT
|
||||||
|
tt_content.list.fields.content.fields.testField.value = TEST VALUE FROM VITEC
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
plugin.tx_vitec.settings.layout = 0
|
||||||
74
packages/vitec/Configuration/TypoScript/setup.typoscript
Normal file
74
packages/vitec/Configuration/TypoScript/setup.typoscript
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
plugin.tx_vitec_usecaseshow {
|
||||||
|
view {
|
||||||
|
templateRootPaths.0 = EXT:vitec/Resources/Private/Templates/
|
||||||
|
partialRootPaths.0 = EXT:vitec/Resources/Private/Partials/
|
||||||
|
layoutRootPaths.0 = EXT:vitec/Resources/Private/Layouts/
|
||||||
|
}
|
||||||
|
persistence {
|
||||||
|
storagePid = 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.tx_vitec_marketshow {
|
||||||
|
view {
|
||||||
|
templateRootPaths.0 = EXT:vitec/Resources/Private/Templates/
|
||||||
|
partialRootPaths.0 = EXT:vitec/Resources/Private/Partials/
|
||||||
|
layoutRootPaths.0 = EXT:vitec/Resources/Private/Layouts/
|
||||||
|
}
|
||||||
|
persistence {
|
||||||
|
storagePid = 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.tx_vitec_solutionshow {
|
||||||
|
view {
|
||||||
|
templateRootPaths.0 = EXT:vitec/Resources/Private/Templates/
|
||||||
|
partialRootPaths.0 = EXT:vitec/Resources/Private/Partials/
|
||||||
|
layoutRootPaths.0 = EXT:vitec/Resources/Private/Layouts/
|
||||||
|
}
|
||||||
|
persistence {
|
||||||
|
storagePid = 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.tx_vitec_usecaselist {
|
||||||
|
view {
|
||||||
|
templateRootPaths.0 = EXT:vitec/Resources/Private/Templates/
|
||||||
|
partialRootPaths.0 = EXT:vitec/Resources/Private/Partials/
|
||||||
|
layoutRootPaths.0 = EXT:vitec/Resources/Private/Layouts/
|
||||||
|
}
|
||||||
|
persistence {
|
||||||
|
storagePid = 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.tx_vitec_Prodcutlist {
|
||||||
|
settings {
|
||||||
|
# Map FlexForm settings to the controller
|
||||||
|
layout = {$plugin.tx_vitec.settings.layout}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.tx_vitec_Prodcutlist {
|
||||||
|
settings {
|
||||||
|
categories = 1,2,3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.tx_extbase {
|
||||||
|
persistence {
|
||||||
|
classes {
|
||||||
|
TYPO3\CMS\Extbase\Domain\Model\Category {
|
||||||
|
subclass = Evomedien\Vitec\Domain\Model\Category
|
||||||
|
}
|
||||||
|
Evomedien\Vitec\Domain\Model\Category {
|
||||||
|
mapping {
|
||||||
|
tableName = sys_category
|
||||||
|
recordType = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include Headless configurations
|
||||||
|
@import 'EXT:vitec/Configuration/TypoScript/Headless/*.typoscript'
|
||||||
|
|
||||||
|
|
||||||
118
packages/vitec/Configuration/page.tsconfig
Normal file
118
packages/vitec/Configuration/page.tsconfig
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# =============================================================
|
||||||
|
# VITEC Extension — page.tsconfig
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
mod {
|
||||||
|
wizards.newContentElement.wizardItems.vitec {
|
||||||
|
header = VITEC
|
||||||
|
show = *
|
||||||
|
elements {
|
||||||
|
# -------- Extbase Plugins --------
|
||||||
|
productlist {
|
||||||
|
iconIdentifier = vitec-plugin-productlist
|
||||||
|
title = List of VITEC Products
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_productlist.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_productlist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
productshow {
|
||||||
|
iconIdentifier = vitec-plugin-productshow
|
||||||
|
title = Show single VITEC Product
|
||||||
|
description = Either show a single product coming from a list, OR select a specific product in flexform
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_productshow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usecaseshow {
|
||||||
|
iconIdentifier = vitec-plugin-usecaseshow
|
||||||
|
title = Show single VITEC Success Story
|
||||||
|
description = Shows a selected Success Story in a Card with different layouts
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_usecaseshow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
marketshow {
|
||||||
|
iconIdentifier = vitec-plugin-marketshow
|
||||||
|
title = Show single VITEC Market
|
||||||
|
description = Shows a selected Market in a Card with different layouts
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_marketshow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
solutionshow {
|
||||||
|
iconIdentifier = vitec-plugin-solutionshow
|
||||||
|
title = Show single VITEC Solution
|
||||||
|
description = Shows a selected Solution in a Card with different layouts
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_solutionshow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usecaselist {
|
||||||
|
iconIdentifier = vitec-plugin-usecaselist
|
||||||
|
title = Show all VITEC Success Stories
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_usecaselist.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_usecaselist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downloadcard {
|
||||||
|
iconIdentifier = vitec-plugin-downloadcard
|
||||||
|
title = Downloadcard for single Download
|
||||||
|
description = LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_usecaselist.description
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_downloadcard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downloadcardcollection {
|
||||||
|
iconIdentifier = vitec-plugin-downloadcardcollection
|
||||||
|
title = Downloadcard Collection
|
||||||
|
description = Multiple Downloads in one Card
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_downloadcardcollection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datasheets {
|
||||||
|
iconIdentifier = vitec-plugin-datasheets
|
||||||
|
title = VITEC Datasheets
|
||||||
|
description = Interactive datasheets with filtering and download functionality
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_datasheets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simplecard {
|
||||||
|
iconIdentifier = vitec-plugin-simplecard
|
||||||
|
title = Simple Card
|
||||||
|
description = Simple Card (Image, Title, Text, Link)
|
||||||
|
tt_content_defValues {
|
||||||
|
CType = list
|
||||||
|
list_type = vitec_simplecard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Backend Previews für klassische Plugins (list_type)
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
mod.web_layout.tt_content.preview {
|
||||||
|
simplecard = EXT:vitec/Resources/Private/Templates/Simplecard/Backendpreview.html
|
||||||
|
# vitec_usecaseshow = EXT:vitec/Resources/Private/Templates/Usecaseshow/Backendpreview.html
|
||||||
|
# ^ Pfad anpassen wenn das Template existiert, dann Kommentar entfernen
|
||||||
|
}
|
||||||
|
|
||||||
|
# Hinweis: Content-Blocks-Previews werden automatisch aus
|
||||||
|
# packages/vitec/ContentBlocks/ContentElements/<n>/templates/backend-preview.html geladen.
|
||||||
|
# Keine separate Registrierung nötig.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#F47937" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="2" y="7" width="20" height="10" rx="2"/>
|
||||||
|
<rect x="6" y="10" width="6" height="4" rx="1" fill="#F47937"/>
|
||||||
|
<path d="M15 12h3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 340 B |
@@ -0,0 +1,74 @@
|
|||||||
|
name: vitec/cta-banner
|
||||||
|
group: vitec
|
||||||
|
prefixFields: true
|
||||||
|
prefixType: vendor
|
||||||
|
|
||||||
|
fields:
|
||||||
|
- identifier: header
|
||||||
|
useExistingField: true
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- identifier: bodytext
|
||||||
|
useExistingField: true
|
||||||
|
enableRichtext: true
|
||||||
|
|
||||||
|
- identifier: primary_cta
|
||||||
|
type: Link
|
||||||
|
required: true
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: primary_cta_label
|
||||||
|
type: Text
|
||||||
|
required: true
|
||||||
|
default: 'Get in touch'
|
||||||
|
max: 30
|
||||||
|
|
||||||
|
- identifier: secondary_cta
|
||||||
|
type: Link
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: secondary_cta_label
|
||||||
|
type: Text
|
||||||
|
max: 30
|
||||||
|
|
||||||
|
- identifier: layout_variant
|
||||||
|
type: Select
|
||||||
|
renderType: selectSingle
|
||||||
|
default: centered
|
||||||
|
items:
|
||||||
|
- label: Centered (text over background)
|
||||||
|
value: centered
|
||||||
|
- label: Split (text left, CTAs right)
|
||||||
|
value: split
|
||||||
|
- label: Stacked (text top, CTAs below)
|
||||||
|
value: stacked
|
||||||
|
|
||||||
|
- identifier: background_variant
|
||||||
|
type: Select
|
||||||
|
renderType: selectSingle
|
||||||
|
default: orange
|
||||||
|
items:
|
||||||
|
- label: Orange
|
||||||
|
value: orange
|
||||||
|
- label: Blue
|
||||||
|
value: blue
|
||||||
|
- label: Graphite
|
||||||
|
value: graphite
|
||||||
|
- label: Midnight
|
||||||
|
value: midnight
|
||||||
|
- label: Light (neutral)
|
||||||
|
value: light
|
||||||
|
|
||||||
|
- identifier: background_image
|
||||||
|
type: File
|
||||||
|
minitems: 0
|
||||||
|
maxitems: 1
|
||||||
|
allowed: common-image-types
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xliff version="1.2">
|
||||||
|
<file source-language="en" datatype="plaintext" original="messages">
|
||||||
|
<body>
|
||||||
|
<trans-unit id="title">
|
||||||
|
<source>VITEC · CTA Banner</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="description">
|
||||||
|
<source>Call-to-Action Banner mit optionalem zweiten Button und Background-Varianten</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="primary_cta.label">
|
||||||
|
<source>Primary CTA Link</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="primary_cta_label.label">
|
||||||
|
<source>Primary CTA Button Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="secondary_cta.label">
|
||||||
|
<source>Secondary CTA Link (optional)</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="secondary_cta_label.label">
|
||||||
|
<source>Secondary CTA Button Text (optional)</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="layout_variant.label">
|
||||||
|
<source>Layout Variant</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="background_variant.label">
|
||||||
|
<source>Background Variant</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="background_image.label">
|
||||||
|
<source>Background Image (optional)</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="background_image.description">
|
||||||
|
<source>Optionales Hintergrundbild — überlagert die Background-Variant-Farbe</source>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:layout name="Preview"/>
|
||||||
|
<f:section name="Content">
|
||||||
|
|
||||||
|
<f:asset.css identifier="vitec-backend-preview" href="EXT:vitec/Resources/Public/Css/backend-preview.css"/>
|
||||||
|
|
||||||
|
<div class="vitec-preview vitec-preview--{data.background_variant}">
|
||||||
|
|
||||||
|
<f:comment>Thumbnail (Background Image oder Farb-Placeholder)</f:comment>
|
||||||
|
<f:if condition="{data.background_image.0}">
|
||||||
|
<f:then>
|
||||||
|
<f:image image="{data.background_image.0}"
|
||||||
|
class="vitec-preview__thumb"
|
||||||
|
width="120c"
|
||||||
|
height="80c"
|
||||||
|
alt="Background Preview"/>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<div class="vitec-preview__thumb-placeholder">
|
||||||
|
<f:switch expression="{data.background_variant}">
|
||||||
|
<f:case value="orange">🟠</f:case>
|
||||||
|
<f:case value="blue">🔵</f:case>
|
||||||
|
<f:case value="graphite">⬛</f:case>
|
||||||
|
<f:case value="midnight">⬛</f:case>
|
||||||
|
<f:case value="light">⬜</f:case>
|
||||||
|
<f:defaultCase>No BG</f:defaultCase>
|
||||||
|
</f:switch>
|
||||||
|
</div>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:comment>Body</f:comment>
|
||||||
|
<div class="vitec-preview__body">
|
||||||
|
<div class="vitec-preview__label">VITEC · CTA Banner</div>
|
||||||
|
|
||||||
|
<h3 class="vitec-preview__headline">
|
||||||
|
<f:if condition="{data.header}">
|
||||||
|
<f:then>{data.header}</f:then>
|
||||||
|
<f:else>
|
||||||
|
<em style="color:#c00;">⚠ Headline fehlt</em>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<f:if condition="{data.bodytext}">
|
||||||
|
<div class="vitec-preview__body-text">
|
||||||
|
<f:format.stripTags>{data.bodytext}</f:format.stripTags>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<div class="vitec-preview__ctas">
|
||||||
|
<f:if condition="{data.primary_cta_label}">
|
||||||
|
<span class="vitec-preview__cta-btn">{data.primary_cta_label} →</span>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{data.secondary_cta_label}">
|
||||||
|
<span class="vitec-preview__cta-btn vitec-preview__cta-btn--secondary">
|
||||||
|
{data.secondary_cta_label}
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<f:comment>Settings Sidebar</f:comment>
|
||||||
|
<div class="vitec-preview__settings">
|
||||||
|
<f:comment>Background Badge</f:comment>
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<span class="vitec-badge__dot vitec-badge__dot--{data.background_variant}"></span>
|
||||||
|
<f:switch expression="{data.background_variant}">
|
||||||
|
<f:case value="orange">VITEC Orange</f:case>
|
||||||
|
<f:case value="blue">VITEC Blue</f:case>
|
||||||
|
<f:case value="graphite">VITEC Graphite</f:case>
|
||||||
|
<f:case value="midnight">VITEC Midnight</f:case>
|
||||||
|
<f:case value="light">VITEC Light</f:case>
|
||||||
|
<f:defaultCase>{data.background_variant}</f:defaultCase>
|
||||||
|
</f:switch>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<f:comment>Layout Variant Badge mit Icon</f:comment>
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<f:switch expression="{data.layout_variant}">
|
||||||
|
<f:case value="centered">◎ Centered</f:case>
|
||||||
|
<f:case value="split">⇄ Split</f:case>
|
||||||
|
<f:case value="stacked">≡ Stacked</f:case>
|
||||||
|
<f:defaultCase>{data.layout_variant}</f:defaultCase>
|
||||||
|
</f:switch>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<f:comment>Warnung wenn Primary-CTA-Link fehlt</f:comment>
|
||||||
|
<f:if condition="{data.primary_cta.url} == ''">
|
||||||
|
<span class="vitec-badge vitec-badge--warning">⚠ Kein Primary-Link</span>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</f:section>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<!-- Headless mode: JSON is built by nb-headless-content-blocks -->
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#F47937" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||||||
|
<path d="M3 9h18"/>
|
||||||
|
<circle cx="8" cy="15" r="2"/>
|
||||||
|
<path d="M13 15h5M13 18h3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 343 B |
@@ -0,0 +1,60 @@
|
|||||||
|
name: vitec/herosection
|
||||||
|
group: vitec
|
||||||
|
prefixFields: true
|
||||||
|
prefixType: vendor
|
||||||
|
|
||||||
|
fields:
|
||||||
|
- identifier: eyebrow
|
||||||
|
type: Text
|
||||||
|
max: 50
|
||||||
|
|
||||||
|
- identifier: header
|
||||||
|
useExistingField: true
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- identifier: subheader
|
||||||
|
useExistingField: true
|
||||||
|
|
||||||
|
- identifier: bodytext
|
||||||
|
useExistingField: true
|
||||||
|
enableRichtext: true
|
||||||
|
|
||||||
|
- identifier: cta
|
||||||
|
type: Link
|
||||||
|
allowedTypes:
|
||||||
|
- page
|
||||||
|
- url
|
||||||
|
- file
|
||||||
|
- email
|
||||||
|
|
||||||
|
- identifier: cta_label
|
||||||
|
type: Text
|
||||||
|
default: 'Learn more'
|
||||||
|
max: 30
|
||||||
|
|
||||||
|
- identifier: hero_image
|
||||||
|
type: File
|
||||||
|
minitems: 0
|
||||||
|
maxitems: 1
|
||||||
|
allowed: common-image-types
|
||||||
|
extendedPalette: true
|
||||||
|
|
||||||
|
- identifier: background_variant
|
||||||
|
type: Select
|
||||||
|
renderType: selectSingle
|
||||||
|
default: none
|
||||||
|
items:
|
||||||
|
- label: None
|
||||||
|
value: none
|
||||||
|
- label: Orange
|
||||||
|
value: orange
|
||||||
|
- label: Blue
|
||||||
|
value: blue
|
||||||
|
- label: Graphite
|
||||||
|
value: graphite
|
||||||
|
- label: Midnight
|
||||||
|
value: midnight
|
||||||
|
|
||||||
|
- identifier: show_logo_wall
|
||||||
|
type: Checkbox
|
||||||
|
default: 0
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xliff version="1.2">
|
||||||
|
<file source-language="en" datatype="plaintext" original="messages">
|
||||||
|
<body>
|
||||||
|
<!-- Element-Labels -->
|
||||||
|
<trans-unit id="title">
|
||||||
|
<source>VITEC · Hero Section</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="description">
|
||||||
|
<source>Haupt-Hero mit Headline, CTA, Background-Variante</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<!-- Field-Labels -->
|
||||||
|
<trans-unit id="eyebrow.label">
|
||||||
|
<source>Eyebrow Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="eyebrow.description">
|
||||||
|
<source>Small Label-Text above the Headline</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="cta.label">
|
||||||
|
<source>CTA Link</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="cta_label.label">
|
||||||
|
<source>CTA Button Text</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="hero_image.label">
|
||||||
|
<source>Hero Image</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="background_variant.label">
|
||||||
|
<source>Background Variant</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
|
<trans-unit id="show_logo_wall.label">
|
||||||
|
<source>Show Logo Wall below</source>
|
||||||
|
</trans-unit>
|
||||||
|
</body>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:layout name="Preview"/>
|
||||||
|
<f:section name="Content">
|
||||||
|
|
||||||
|
<f:asset.css identifier="vitec-backend-preview" href="EXT:vitec/Resources/Public/Css/backend-preview.css"/>
|
||||||
|
|
||||||
|
<div class="vitec-preview vitec-preview--{data.background_variant}">
|
||||||
|
|
||||||
|
<f:comment>Thumbnail</f:comment>
|
||||||
|
<f:if condition="{data.hero_image.0}">
|
||||||
|
<f:then>
|
||||||
|
<f:image image="{data.hero_image.0}"
|
||||||
|
class="vitec-preview__thumb"
|
||||||
|
width="120c"
|
||||||
|
height="80c"
|
||||||
|
alt="Hero Image Preview"/>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<div class="vitec-preview__thumb-placeholder">No Image</div>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<div class="vitec-preview__body">
|
||||||
|
<div class="vitec-preview__label">VITEC · Hero Section</div>
|
||||||
|
|
||||||
|
<f:if condition="{data.eyebrow}">
|
||||||
|
<div class="vitec-preview__eyebrow">{data.eyebrow}</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<h3 class="vitec-preview__headline">
|
||||||
|
<f:if condition="{data.header}">
|
||||||
|
<f:then>{data.header}</f:then>
|
||||||
|
<f:else>
|
||||||
|
<em style="color:#c00;">⚠ Headline fehlt</em>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<f:if condition="{data.subheader}">
|
||||||
|
<div class="vitec-preview__subline">{data.subheader}</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.bodytext}">
|
||||||
|
<div class="vitec-preview__body-text">
|
||||||
|
<f:format.stripTags>{data.bodytext}</f:format.stripTags>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.cta_label}">
|
||||||
|
<div class="vitec-preview__ctas">
|
||||||
|
<span class="vitec-preview__cta-btn">{data.cta_label} →</span>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<f:comment>Settings Sidebar (ohne Partial)</f:comment>
|
||||||
|
<div class="vitec-preview__settings">
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<span class="vitec-badge__dot vitec-badge__dot--{data.background_variant}"></span>
|
||||||
|
Background: {data.background_variant}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<f:if condition="{data.show_logo_wall}">
|
||||||
|
<span class="vitec-badge">🏢 Logo Wall</span>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{data.hero_image.0} == ''">
|
||||||
|
<span class="vitec-badge vitec-badge--warning">⚠ Kein Bild</span>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</f:section>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<!-- Headless mode: output handled by nb-headless-content-blocks, this template is intentionally minimal -->
|
||||||
|
</html>
|
||||||
15
packages/vitec/ContentBlocks/Shared/BackgroundBadge.html
Normal file
15
packages/vitec/ContentBlocks/Shared/BackgroundBadge.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:spaceless>
|
||||||
|
<span class="vitec-badge">
|
||||||
|
<span class="vitec-badge__dot vitec-badge__dot--{variant}"></span>
|
||||||
|
<f:switch expression="{variant}">
|
||||||
|
<f:case value="vitecblue">VITEC-Blue</f:case>
|
||||||
|
<f:case value="vitecorange">VITEC-Orange</f:case>
|
||||||
|
<f:case value="vitecgraphite">VITEC-Graphite</f:case>
|
||||||
|
<f:case value="vitecmidnight">VITEC-Midnight</f:case>
|
||||||
|
<f:case value="viteclight">VITEC-Light</f:case>
|
||||||
|
<f:defaultCase>{variant}</f:defaultCase>
|
||||||
|
</f:switch>
|
||||||
|
</span>
|
||||||
|
</f:spaceless>
|
||||||
|
</html>
|
||||||
307
packages/vitec/Documentation/HeadlessIntegration.md
Normal file
307
packages/vitec/Documentation/HeadlessIntegration.md
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
# Headless Integration Guide for TYPO3 13 Custom Plugins
|
||||||
|
|
||||||
|
This guide explains how to integrate a custom TYPO3 Extbase plugin with the TYPO3 Headless extension to provide JSON API output.
|
||||||
|
|
||||||
|
## The Challenge
|
||||||
|
|
||||||
|
When integrating custom plugins with TYPO3 Headless, you face two main obstacles:
|
||||||
|
|
||||||
|
1. **Lost Context**: Headless creates new ContentObjectRenderer contexts when rendering JSON fields, causing the content element context to be lost. Standard methods like `$conf['parentObj']` or `$GLOBALS['TSFE']->cObj->data` will only contain page data, not the content element.
|
||||||
|
|
||||||
|
2. **Extbase Limitations**: Extbase repositories don't work in UserFunc context because the full Extbase framework isn't bootstrapped during headless JSON rendering.
|
||||||
|
|
||||||
|
## The Solution
|
||||||
|
|
||||||
|
### Step 1: Override Headless TypoScript in Site Set
|
||||||
|
|
||||||
|
Create or modify your site set's `setup.typoscript` file to add custom fields to the headless JSON output:
|
||||||
|
|
||||||
|
**File**: `packages/yourext/Configuration/Sets/YourSetName/setup.typoscript`
|
||||||
|
|
||||||
|
```typoscript
|
||||||
|
# Override headless rendering for list plugins
|
||||||
|
tt_content.list.fields.content.fields.yourCustomField = USER
|
||||||
|
tt_content.list.fields.content.fields.yourCustomField {
|
||||||
|
userFunc = Vendor\YourExt\UserFunc\YourJsonRenderer->render
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why in site set?** Site sets load AFTER headless TypoScript, allowing you to override the default configuration.
|
||||||
|
|
||||||
|
### Step 2: Create a UserFunc Renderer
|
||||||
|
|
||||||
|
Create a UserFunc class that will render your plugin data as JSON:
|
||||||
|
|
||||||
|
**File**: `packages/yourext/Classes/UserFunc/YourJsonRenderer.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Vendor\YourExt\UserFunc;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
class YourJsonRenderer
|
||||||
|
{
|
||||||
|
public function render(string $content, array $conf): string
|
||||||
|
{
|
||||||
|
// Step 1: Query tt_content to find the plugin configuration
|
||||||
|
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
|
||||||
|
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tt_content');
|
||||||
|
|
||||||
|
$contentElements = $queryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tt_content')
|
||||||
|
->where(
|
||||||
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
||||||
|
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('yourext_pluginname', ParameterType::STRING)),
|
||||||
|
$queryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$queryBuilder->expr()->eq('hidden', 0)
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
if (empty($contentElements)) {
|
||||||
|
return json_encode([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentElement = $contentElements[0];
|
||||||
|
|
||||||
|
// Step 2: Parse FlexForm settings
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Step 3: Use direct database queries (NOT Extbase repositories)
|
||||||
|
$dataQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_yourext_domain_model_yourmodel');
|
||||||
|
|
||||||
|
$records = $dataQueryBuilder
|
||||||
|
->select('*')
|
||||||
|
->from('tx_yourext_domain_model_yourmodel')
|
||||||
|
->where(
|
||||||
|
$dataQueryBuilder->expr()->eq('deleted', 0),
|
||||||
|
$dataQueryBuilder->expr()->eq('hidden', 0)
|
||||||
|
// Add your filters based on $settings
|
||||||
|
)
|
||||||
|
->executeQuery()
|
||||||
|
->fetchAllAssociative();
|
||||||
|
|
||||||
|
// Step 4: Serialize to JSON
|
||||||
|
$data = [];
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$data[] = [
|
||||||
|
'uid' => (int)$record['uid'],
|
||||||
|
'title' => $record['title'] ?? '',
|
||||||
|
// Add more fields as needed
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Using FlexForm Settings
|
||||||
|
|
||||||
|
You can access any FlexForm field from your plugin configuration:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Parse FlexForm
|
||||||
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||||
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
||||||
|
$settings = $flexFormData['settings'] ?? [];
|
||||||
|
|
||||||
|
// Access specific settings
|
||||||
|
$categoryUids = array_filter(
|
||||||
|
array_map('intval', explode(',', (string)($settings['categories'] ?? '')))
|
||||||
|
);
|
||||||
|
$debugMode = (bool)($settings['debug'] ?? false);
|
||||||
|
|
||||||
|
// Use settings in your query
|
||||||
|
if (!empty($categoryUids)) {
|
||||||
|
// Add filtering based on categories
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Conditional Debug Output
|
||||||
|
|
||||||
|
You can add debug information based on a FlexForm checkbox:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// At the end of your render method:
|
||||||
|
if ($debugMode) {
|
||||||
|
return json_encode([
|
||||||
|
'products' => $data,
|
||||||
|
'debug' => [
|
||||||
|
'pageId' => $pageId,
|
||||||
|
'categoryUids' => $categoryUids,
|
||||||
|
'recordCount' => count($data),
|
||||||
|
'settings' => $settings // Show all settings for troubleshooting
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return just the data array when debug is off
|
||||||
|
return json_encode($data);
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows you to toggle debug information on/off directly in the backend without code changes.
|
||||||
|
|
||||||
|
### Step 4: Important Notes
|
||||||
|
|
||||||
|
#### Use Doctrine ParameterType (TYPO3 13)
|
||||||
|
|
||||||
|
In TYPO3 13, always use `Doctrine\DBAL\ParameterType` instead of `\PDO::PARAM_*`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
|
||||||
|
// Correct for TYPO3 13:
|
||||||
|
$queryBuilder->createNamedParameter($value, ParameterType::INTEGER)
|
||||||
|
$queryBuilder->createNamedParameter($value, ParameterType::STRING)
|
||||||
|
|
||||||
|
// Wrong (deprecated):
|
||||||
|
$queryBuilder->createNamedParameter($value, \PDO::PARAM_INT)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Why Direct Database Queries?
|
||||||
|
|
||||||
|
**Don't use**:
|
||||||
|
```php
|
||||||
|
// This won't work in UserFunc context
|
||||||
|
$repository = GeneralUtility::makeInstance(YourRepository::class);
|
||||||
|
$records = $repository->findAll(); // Returns empty!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use instead**:
|
||||||
|
```php
|
||||||
|
// Direct database query
|
||||||
|
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
|
||||||
|
->getQueryBuilderForTable('tx_yourext_domain_model_yourmodel');
|
||||||
|
$records = $queryBuilder->select('*')->from('tx_yourext_domain_model_yourmodel')->...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Clear Cache
|
||||||
|
|
||||||
|
After making changes, always clear the TYPO3 cache:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./vendor/bin/typo3 cache:flush
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Test your JSON output using curl and jq:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if your field appears
|
||||||
|
curl -s "https://yourdomain.com/your-page" | jq '.content.colPos0[] | select(.type=="your_plugin") | .content'
|
||||||
|
|
||||||
|
# Check specific field
|
||||||
|
curl -s "https://yourdomain.com/your-page" | jq '.content.colPos0[] | select(.type=="your_plugin") | .content.yourCustomField'
|
||||||
|
|
||||||
|
# With debug mode enabled in backend, you'll see:
|
||||||
|
curl -s "https://yourdomain.com/your-page" | jq '.content.colPos0[] | select(.type=="your_plugin") | .content.yourCustomField.debug'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
**Without debug mode**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"uid": 1,
|
||||||
|
"title": "Product Name",
|
||||||
|
"slug": "product-name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**With debug mode enabled** (checkbox in backend):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"uid": 1,
|
||||||
|
"title": "Product Name",
|
||||||
|
"slug": "product-name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"pageId": 5,
|
||||||
|
"categoryUids": [8],
|
||||||
|
"productCount": 1,
|
||||||
|
"settings": {
|
||||||
|
"debug": "1",
|
||||||
|
"categories": "8",
|
||||||
|
"layout": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: Real-World Implementation
|
||||||
|
|
||||||
|
See `Classes/UserFunc/ProductListJsonRenderer.php` for a complete working example that:
|
||||||
|
- Queries tt_content for plugin configuration
|
||||||
|
- Parses FlexForm settings (including debug mode, categories)
|
||||||
|
- Queries products with visibility filters using direct database queries
|
||||||
|
- Provides conditional debug output based on FlexForm settings
|
||||||
|
- Serializes product data to JSON with backward compatibility
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Empty Results
|
||||||
|
- **Problem**: UserFunc returns empty array
|
||||||
|
- **Solution**: Check that you're using direct database queries, not Extbase repositories
|
||||||
|
|
||||||
|
### Parameter Type Errors
|
||||||
|
- **Problem**: `createNamedParameter(): Argument #2 ($type) must be of type Doctrine\DBAL\ParameterType`
|
||||||
|
- **Solution**: Use `ParameterType::INTEGER` instead of `\PDO::PARAM_INT`
|
||||||
|
|
||||||
|
### Content Element Not Found
|
||||||
|
- **Problem**: tt_content query returns empty
|
||||||
|
- **Solution**: Verify the `list_type` value matches your plugin signature exactly
|
||||||
|
|
||||||
|
### Wrong Context Data
|
||||||
|
- **Problem**: Trying to access content element via `$GLOBALS['TSFE']->cObj->data`
|
||||||
|
- **Solution**: This only contains page data. Query tt_content directly instead
|
||||||
|
|
||||||
|
## Architecture Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Browser requests JSON page
|
||||||
|
↓
|
||||||
|
2. Headless extension renders page as JSON
|
||||||
|
↓
|
||||||
|
3. Encounters tt_content.list (your plugin)
|
||||||
|
↓
|
||||||
|
4. Executes your TypoScript override
|
||||||
|
↓
|
||||||
|
5. Calls your UserFunc
|
||||||
|
↓
|
||||||
|
6. UserFunc queries tt_content for plugin config
|
||||||
|
↓
|
||||||
|
7. UserFunc queries database directly for records
|
||||||
|
↓
|
||||||
|
8. Returns JSON array
|
||||||
|
↓
|
||||||
|
9. Headless includes it in final JSON output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The key insight is that headless rendering requires a different approach than normal Extbase plugins:
|
||||||
|
- Use site sets for TypoScript overrides
|
||||||
|
- Use UserFuncs for custom rendering
|
||||||
|
- Use direct database queries instead of repositories
|
||||||
|
- Query tt_content to recover plugin configuration
|
||||||
|
|
||||||
|
This approach provides full control over JSON output while working within the constraints of the headless rendering context.
|
||||||
380
packages/vitec/ExtensionBuilder.json
Normal file
380
packages/vitec/ExtensionBuilder.json
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
{
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"position": [
|
||||||
|
529,
|
||||||
|
328
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"value": {
|
||||||
|
"actionGroup": {
|
||||||
|
"_default0_index": false,
|
||||||
|
"_default1_list": false,
|
||||||
|
"_default2_show": false,
|
||||||
|
"_default3_new_create": false,
|
||||||
|
"_default4_edit_update": false,
|
||||||
|
"_default5_delete": false,
|
||||||
|
"customActions": []
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"objectsettings": {
|
||||||
|
"addDeletedField": true,
|
||||||
|
"addHiddenField": true,
|
||||||
|
"addStarttimeEndtimeFields": true,
|
||||||
|
"aggregateRoot": false,
|
||||||
|
"controllerScope": "Frontend",
|
||||||
|
"categorizable": false,
|
||||||
|
"description": "",
|
||||||
|
"mapToTable": "",
|
||||||
|
"parentClass": "",
|
||||||
|
"sorting": false,
|
||||||
|
"type": "Entity",
|
||||||
|
"uid": "c5830401-5be8-41c8-be95-191f3c7ef1d5"
|
||||||
|
},
|
||||||
|
"propertyGroup": {
|
||||||
|
"properties": []
|
||||||
|
},
|
||||||
|
"relationGroup": {
|
||||||
|
"relations": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"position": [
|
||||||
|
437,
|
||||||
|
331
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "Product",
|
||||||
|
"value": {
|
||||||
|
"actionGroup": {
|
||||||
|
"_default0_index": true,
|
||||||
|
"_default1_list": true,
|
||||||
|
"_default2_show": true,
|
||||||
|
"_default3_new_create": false,
|
||||||
|
"_default4_edit_update": false,
|
||||||
|
"_default5_delete": false,
|
||||||
|
"customActions": []
|
||||||
|
},
|
||||||
|
"name": "Product",
|
||||||
|
"objectsettings": {
|
||||||
|
"addDeletedField": true,
|
||||||
|
"addHiddenField": true,
|
||||||
|
"addStarttimeEndtimeFields": true,
|
||||||
|
"aggregateRoot": true,
|
||||||
|
"controllerScope": "Frontend",
|
||||||
|
"categorizable": false,
|
||||||
|
"description": "",
|
||||||
|
"mapToTable": "",
|
||||||
|
"parentClass": "",
|
||||||
|
"sorting": false,
|
||||||
|
"type": "Entity",
|
||||||
|
"uid": "79b6c941-ee45-4b49-93d7-0bde2e30593a"
|
||||||
|
},
|
||||||
|
"propertyGroup": {
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"allowedFileTypes": "",
|
||||||
|
"propertyDescription": "",
|
||||||
|
"propertyIsL10nModeExclude": false,
|
||||||
|
"propertyIsNullable": false,
|
||||||
|
"propertyIsRequired": true,
|
||||||
|
"propertyName": "title",
|
||||||
|
"propertyType": "String",
|
||||||
|
"typeSelect": {
|
||||||
|
"selectboxValues": "",
|
||||||
|
"renderType": "selectSingle",
|
||||||
|
"foreignTable": "",
|
||||||
|
"whereClause": ""
|
||||||
|
},
|
||||||
|
"typeText": {
|
||||||
|
"enableRichtext": false
|
||||||
|
},
|
||||||
|
"typeNumber": {
|
||||||
|
"enableSlider": false,
|
||||||
|
"steps": 1,
|
||||||
|
"setRange": false,
|
||||||
|
"upperRange": 255,
|
||||||
|
"lowerRange": 0
|
||||||
|
},
|
||||||
|
"typeColor": {
|
||||||
|
"setValuesColorPicker": false,
|
||||||
|
"colorPickerValues": ""
|
||||||
|
},
|
||||||
|
"typeBoolean": {
|
||||||
|
"renderType": "default",
|
||||||
|
"booleanValues": ""
|
||||||
|
},
|
||||||
|
"typePassword": {
|
||||||
|
"renderPasswordGenerator": false
|
||||||
|
},
|
||||||
|
"typeDateTime": {
|
||||||
|
"dbTypeDateTime": "",
|
||||||
|
"formatDateTime": ""
|
||||||
|
},
|
||||||
|
"typeFile": {
|
||||||
|
"allowedFileTypes": ""
|
||||||
|
},
|
||||||
|
"size": "30",
|
||||||
|
"rows": "10",
|
||||||
|
"minItems": "",
|
||||||
|
"maxItems": "",
|
||||||
|
"uid": "b5773ee7-bb5f-474f-9464-629f42c9b5a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allowedFileTypes": "",
|
||||||
|
"propertyDescription": "",
|
||||||
|
"propertyIsL10nModeExclude": false,
|
||||||
|
"propertyIsNullable": false,
|
||||||
|
"propertyIsRequired": false,
|
||||||
|
"propertyName": "slug",
|
||||||
|
"propertyType": "Slug",
|
||||||
|
"typeSelect": {
|
||||||
|
"selectboxValues": "",
|
||||||
|
"renderType": "selectSingle",
|
||||||
|
"foreignTable": "",
|
||||||
|
"whereClause": ""
|
||||||
|
},
|
||||||
|
"typeText": {
|
||||||
|
"enableRichtext": false
|
||||||
|
},
|
||||||
|
"typeNumber": {
|
||||||
|
"enableSlider": false,
|
||||||
|
"steps": 1,
|
||||||
|
"setRange": false,
|
||||||
|
"upperRange": 255,
|
||||||
|
"lowerRange": 0
|
||||||
|
},
|
||||||
|
"typeColor": {
|
||||||
|
"setValuesColorPicker": false,
|
||||||
|
"colorPickerValues": ""
|
||||||
|
},
|
||||||
|
"typeBoolean": {
|
||||||
|
"renderType": "default",
|
||||||
|
"booleanValues": ""
|
||||||
|
},
|
||||||
|
"typePassword": {
|
||||||
|
"renderPasswordGenerator": false
|
||||||
|
},
|
||||||
|
"typeDateTime": {
|
||||||
|
"dbTypeDateTime": "",
|
||||||
|
"formatDateTime": ""
|
||||||
|
},
|
||||||
|
"typeFile": {
|
||||||
|
"allowedFileTypes": ""
|
||||||
|
},
|
||||||
|
"size": "30",
|
||||||
|
"rows": "10",
|
||||||
|
"minItems": "",
|
||||||
|
"maxItems": "",
|
||||||
|
"uid": "95426fa5-210a-424a-9618-a1b6c8aa6621"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"relationGroup": {
|
||||||
|
"relations": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"backendModules": [],
|
||||||
|
"description": "Manage all VITEC Products and Downloads",
|
||||||
|
"emConf": {
|
||||||
|
"category": "plugin",
|
||||||
|
"custom_category": "",
|
||||||
|
"dependsOn": "typo3 => 12.4.0-12.4.99",
|
||||||
|
"disableLocalization": false,
|
||||||
|
"disableVersioning": false,
|
||||||
|
"generateDocumentationTemplate": false,
|
||||||
|
"generateEditorConfig": true,
|
||||||
|
"generateEmptyGitRepository": true,
|
||||||
|
"sourceLanguage": "en",
|
||||||
|
"state": "alpha",
|
||||||
|
"targetVersion": "12.4",
|
||||||
|
"version": "0.0.1"
|
||||||
|
},
|
||||||
|
"extensionKey": "vitec",
|
||||||
|
"name": "VITEC",
|
||||||
|
"originalExtensionKey": "",
|
||||||
|
"originalVendorName": "",
|
||||||
|
"persons": [],
|
||||||
|
"plugins": [],
|
||||||
|
"vendorName": "Evomedien"
|
||||||
|
},
|
||||||
|
"wires": [],
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "customModel",
|
||||||
|
"position": {
|
||||||
|
"x": 529,
|
||||||
|
"y": 328
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "",
|
||||||
|
"objectType": "",
|
||||||
|
"isAggregateRoot": false,
|
||||||
|
"controllerScope": "Frontend",
|
||||||
|
"enableSorting": false,
|
||||||
|
"addDeletedField": true,
|
||||||
|
"addHiddenField": true,
|
||||||
|
"addStarttimeEndtimeFields": true,
|
||||||
|
"enableCategorization": false,
|
||||||
|
"description": "",
|
||||||
|
"mapToExistingTable": "",
|
||||||
|
"extendExistingModelClass": "",
|
||||||
|
"actions": {
|
||||||
|
"actionIndex": false,
|
||||||
|
"actionList": false,
|
||||||
|
"actionShow": false,
|
||||||
|
"actionNewCreate": false,
|
||||||
|
"actionEditUpdate": false,
|
||||||
|
"actionDelete": false
|
||||||
|
},
|
||||||
|
"customActions": [],
|
||||||
|
"properties": [],
|
||||||
|
"relations": []
|
||||||
|
},
|
||||||
|
"dragHandle": ".drag-handle",
|
||||||
|
"draggable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dndnode_1",
|
||||||
|
"type": "customModel",
|
||||||
|
"position": {
|
||||||
|
"x": 437,
|
||||||
|
"y": 331
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "Product",
|
||||||
|
"objectType": "",
|
||||||
|
"isAggregateRoot": true,
|
||||||
|
"controllerScope": "Frontend",
|
||||||
|
"enableSorting": false,
|
||||||
|
"addDeletedField": true,
|
||||||
|
"addHiddenField": true,
|
||||||
|
"addStarttimeEndtimeFields": true,
|
||||||
|
"enableCategorization": false,
|
||||||
|
"description": "",
|
||||||
|
"mapToExistingTable": "",
|
||||||
|
"extendExistingModelClass": "",
|
||||||
|
"actions": {
|
||||||
|
"actionIndex": true,
|
||||||
|
"actionList": true,
|
||||||
|
"actionShow": true,
|
||||||
|
"actionNewCreate": false,
|
||||||
|
"actionEditUpdate": false,
|
||||||
|
"actionDelete": false
|
||||||
|
},
|
||||||
|
"customActions": [],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "String",
|
||||||
|
"description": "",
|
||||||
|
"isRequired": true,
|
||||||
|
"isNullable": false,
|
||||||
|
"isExcludeField": false,
|
||||||
|
"isl10nModeExlude": false,
|
||||||
|
"typeSelect": {
|
||||||
|
"selectboxValues": "",
|
||||||
|
"renderType": "selectSingle",
|
||||||
|
"foreignTable": ""
|
||||||
|
},
|
||||||
|
"typeText": {
|
||||||
|
"enableRichtext": false
|
||||||
|
},
|
||||||
|
"typeNumber": {
|
||||||
|
"enableSlider": false,
|
||||||
|
"steps": 1,
|
||||||
|
"setRange": false,
|
||||||
|
"upperRange": 255,
|
||||||
|
"lowerRange": 0
|
||||||
|
},
|
||||||
|
"typeColor": {
|
||||||
|
"setValuesColorPicker": false,
|
||||||
|
"colorPickerValues": ""
|
||||||
|
},
|
||||||
|
"typeBoolean": {
|
||||||
|
"renderType": "default",
|
||||||
|
"booleanValues": ""
|
||||||
|
},
|
||||||
|
"typePassword": {
|
||||||
|
"renderPasswordGenerator": false
|
||||||
|
},
|
||||||
|
"typeDateTime": {
|
||||||
|
"dbTypeDateTime": "",
|
||||||
|
"formatDateTime": ""
|
||||||
|
},
|
||||||
|
"typeFile": {
|
||||||
|
"allowedFileTypes": ""
|
||||||
|
},
|
||||||
|
"size": "",
|
||||||
|
"minItems": "",
|
||||||
|
"maxItems": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "slug",
|
||||||
|
"type": "Slug",
|
||||||
|
"description": "",
|
||||||
|
"isRequired": false,
|
||||||
|
"isNullable": false,
|
||||||
|
"isExcludeField": false,
|
||||||
|
"isl10nModeExlude": false,
|
||||||
|
"typeSelect": {
|
||||||
|
"selectboxValues": "",
|
||||||
|
"renderType": "selectSingle",
|
||||||
|
"foreignTable": ""
|
||||||
|
},
|
||||||
|
"typeText": {
|
||||||
|
"enableRichtext": false
|
||||||
|
},
|
||||||
|
"typeNumber": {
|
||||||
|
"enableSlider": false,
|
||||||
|
"steps": 1,
|
||||||
|
"setRange": false,
|
||||||
|
"upperRange": 255,
|
||||||
|
"lowerRange": 0
|
||||||
|
},
|
||||||
|
"typeColor": {
|
||||||
|
"setValuesColorPicker": false,
|
||||||
|
"colorPickerValues": ""
|
||||||
|
},
|
||||||
|
"typeBoolean": {
|
||||||
|
"renderType": "default",
|
||||||
|
"booleanValues": ""
|
||||||
|
},
|
||||||
|
"typePassword": {
|
||||||
|
"renderPasswordGenerator": false
|
||||||
|
},
|
||||||
|
"typeDateTime": {
|
||||||
|
"dbTypeDateTime": "",
|
||||||
|
"formatDateTime": ""
|
||||||
|
},
|
||||||
|
"typeFile": {
|
||||||
|
"allowedFileTypes": ""
|
||||||
|
},
|
||||||
|
"size": "",
|
||||||
|
"minItems": "",
|
||||||
|
"maxItems": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
|
},
|
||||||
|
"dragHandle": ".drag-handle",
|
||||||
|
"draggable": true,
|
||||||
|
"width": 300,
|
||||||
|
"height": 1170
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [],
|
||||||
|
"storagePath": "\/usr\/www\/users\/vitecxxx\/live\/extensions\/",
|
||||||
|
"log": {
|
||||||
|
"last_modified": "2025-01-13 03:54",
|
||||||
|
"extension_builder_version": "v12.0.0-beta.2",
|
||||||
|
"be_user": " (1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
0
packages/vitec/Readme.MD
Normal file
0
packages/vitec/Readme.MD
Normal file
11
packages/vitec/Resources/Private/.htaccess
Normal file
11
packages/vitec/Resources/Private/.htaccess
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Apache < 2.3
|
||||||
|
<IfModule !mod_authz_core.c>
|
||||||
|
Order allow,deny
|
||||||
|
Deny from all
|
||||||
|
Satisfy All
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Apache >= 2.3
|
||||||
|
<IfModule mod_authz_core.c>
|
||||||
|
Require all denied
|
||||||
|
</IfModule>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<f:argument name="variant" type="string" optional="{true}" default="primary" />
|
||||||
|
|
||||||
|
<button class="myButton myButton--{variant}">
|
||||||
|
<f:slot />
|
||||||
|
</button>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user