Checkpoint: headless JSON architecture documented + cleanup (Stufe 1+2)

Restore point before Stufe 3 (central resolver-service refactoring).

Includes this session's work:
- Success Story (usecase) rebuild: new fields/tabs, UsecaseSerializer,
  thin List/Show renderers, MM relations, inline content elements.
- vitec_columns content block (two-column layout with per-item content)
  incl. unified header section; special-cased JSON resolution.
- Container per-column flex (items[].config align/justify) + gap on parent.
- Unified header section across CEs/plugins/containers; header fields in JSON.
- ISO-style architecture spec: Documentation/Headless-JSON-Architecture.md.
- Cleanup: removed local scratch + verified .bak backups.
- Fixes: B-6 (undefined thumbnail variable in Downloadcardcollection image
  resolver), B-7 (unsatisfiable legacy CType OR branch in Downloadcard/Datasheets).

Rollback: git reset --hard snapshot-2026-07-09-pre-stufe3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
o-rascheandClaude Opus 4.8 committed 2026-07-09 10:01:56 +02:00
1 parent c6759186cb
commit bb7c03235d
237 files changed
+6698 -1749

No files matched your search

@@ -0,0 +1,266 @@
<?php
namespace Evomedien\Vitec\Domain\Model;
use TYPO3\CMS\Extbase\Domain\Model\Category;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
class Event extends AbstractEntity
{
/** @var string */
protected $title = '';
/** @var string */
protected $slug = '';
/** @var string */
protected $teaser = '';
/** @var string */
protected $description = '';
/** @var \DateTime|null */
protected $eventstart;
/** @var \DateTime|null */
protected $eventend;
/** @var string */
protected $venue = '';
/** @var string */
protected $booth = '';
/** @var string */
protected $city = '';
/** @var string */
protected $country = '';
/** @var string */
protected $attendancemode = 'offline';
/** @var string */
protected $eventstatus = 'scheduled';
/** @var string */
protected $eventurl = '';
/** @var string */
protected $meetinglink = '';
/**
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference|null
* @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
*/
protected $image;
/** @var bool */
protected $hideonwebsite = false;
/** @var bool */
protected $hideonapp = false;
/**
* @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();
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getSlug(): string
{
return $this->slug;
}
public function setSlug(string $slug): void
{
$this->slug = $slug;
}
public function getTeaser(): string
{
return $this->teaser;
}
public function setTeaser(string $teaser): void
{
$this->teaser = $teaser;
}
public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): void
{
$this->description = $description;
}
public function getEventstart(): ?\DateTime
{
return $this->eventstart;
}
public function setEventstart(?\DateTime $eventstart): void
{
$this->eventstart = $eventstart;
}
public function getEventend(): ?\DateTime
{
return $this->eventend;
}
public function setEventend(?\DateTime $eventend): void
{
$this->eventend = $eventend;
}
public function getVenue(): string
{
return $this->venue;
}
public function setVenue(string $venue): void
{
$this->venue = $venue;
}
public function getBooth(): string
{
return $this->booth;
}
public function setBooth(string $booth): void
{
$this->booth = $booth;
}
public function getCity(): string
{
return $this->city;
}
public function setCity(string $city): void
{
$this->city = $city;
}
public function getCountry(): string
{
return $this->country;
}
public function setCountry(string $country): void
{
$this->country = $country;
}
public function getAttendancemode(): string
{
return $this->attendancemode;
}
public function setAttendancemode(string $attendancemode): void
{
$this->attendancemode = $attendancemode;
}
public function getEventstatus(): string
{
return $this->eventstatus;
}
public function setEventstatus(string $eventstatus): void
{
$this->eventstatus = $eventstatus;
}
public function getEventurl(): string
{
return $this->eventurl;
}
public function setEventurl(string $eventurl): void
{
$this->eventurl = $eventurl;
}
public function getMeetinglink(): string
{
return $this->meetinglink;
}
public function setMeetinglink(string $meetinglink): void
{
$this->meetinglink = $meetinglink;
}
public function getImage(): ?FileReference
{
return $this->image;
}
public function setImage(?FileReference $image): void
{
$this->image = $image;
}
public function getHideonwebsite(): bool
{
return $this->hideonwebsite;
}
public function setHideonwebsite(bool $hideonwebsite): void
{
$this->hideonwebsite = $hideonwebsite;
}
public function getHideonapp(): bool
{
return $this->hideonapp;
}
public function setHideonapp(bool $hideonapp): void
{
$this->hideonapp = $hideonapp;
}
public function addCategory(Category $category): void
{
$this->categories->attach($category);
}
public function removeCategory(Category $categoryToRemove): void
{
$this->categories->detach($categoryToRemove);
}
public function getCategories(): ObjectStorage
{
return $this->categories;
}
public function setCategories(ObjectStorage $categories): void
{
$this->categories = $categories;
}
}
View File
File mode changed.
@@ -0,0 +1,71 @@
<?php
namespace Evomedien\Vitec\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* The repository for Event
*/
class EventRepository extends Repository
{
protected $defaultOrderings = [
'eventstart' => QueryInterface::ORDER_ASCENDING,
];
/**
* All upcoming (or currently running) events, soonest first.
* "Upcoming" = event end (or start, when no end is set) is today or later.
*/
public function findUpcoming(): QueryResultInterface
{
$todayMidnight = strtotime('today');
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->matching(
$query->logicalAnd(
$query->equals('hideonwebsite', 0),
$query->logicalOr(
$query->greaterThanOrEqual('eventend', $todayMidnight),
$query->logicalAnd(
$query->equals('eventend', 0),
$query->greaterThanOrEqual('eventstart', $todayMidnight)
)
)
)
);
return $query->execute();
}
/**
* Past events, most recent first.
*/
public function findPast(): QueryResultInterface
{
$todayMidnight = strtotime('today');
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->matching(
$query->logicalAnd(
$query->equals('hideonwebsite', 0),
$query->logicalOr(
$query->logicalAnd(
$query->greaterThan('eventend', 0),
$query->lessThan('eventend', $todayMidnight)
),
$query->logicalAnd(
$query->equals('eventend', 0),
$query->greaterThan('eventstart', 0),
$query->lessThan('eventstart', $todayMidnight)
)
)
)
);
$query->setOrderings(['eventstart' => QueryInterface::ORDER_DESCENDING]);
return $query->execute();
}
}