mirror of
https://github.com/werkraum-media/thuecat.git
synced 2024-12-05 03:26:13 +01:00
Add support for additional images added via TYPO3 (#112)
Some installations might need to add further images to records imported from ThüCAT. The records are now extended to support adding images by editors. The images are not touched during import. The images are also ignored during clean ups, the editor is in full control. This feature for now is only added to tourist attractions by default. The feature is implemented in a way that all objects extending the ``WerkraumMedia\ThueCat\Domain\Model\Frontend\Base`` class are usable by adding an ``editorial_images`` field to their table.
This commit is contained in:
parent
2a39dc7753
commit
8663d5a759
25 changed files with 897 additions and 14 deletions
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
|
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Resource\FileReference;
|
||||||
use TYPO3\CMS\Core\Type\TypeInterface;
|
use TYPO3\CMS\Core\Type\TypeInterface;
|
||||||
|
|
||||||
class Media implements TypeInterface
|
class Media implements TypeInterface
|
||||||
|
@ -37,6 +38,11 @@ class Media implements TypeInterface
|
||||||
*/
|
*/
|
||||||
private $data;
|
private $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FileReference[]
|
||||||
|
*/
|
||||||
|
protected $editorialImages = [];
|
||||||
|
|
||||||
public function __construct(string $serialized)
|
public function __construct(string $serialized)
|
||||||
{
|
{
|
||||||
$this->serialized = $serialized;
|
$this->serialized = $serialized;
|
||||||
|
@ -73,6 +79,28 @@ class Media implements TypeInterface
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAllImages(): array
|
||||||
|
{
|
||||||
|
return array_merge($this->getEditorialImages(), $this->getImages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FileReference[]
|
||||||
|
*/
|
||||||
|
public function getEditorialImages(): array
|
||||||
|
{
|
||||||
|
return $this->editorialImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Only used to set the values while mapping objects.
|
||||||
|
* @see: AfterObjectThawedHandler
|
||||||
|
*/
|
||||||
|
public function setEditorialImages(array $images): void
|
||||||
|
{
|
||||||
|
$this->editorialImages = $images;
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
return $this->serialized;
|
return $this->serialized;
|
||||||
|
|
108
Classes/Typo3/Extbase/DataMapping/AfterObjectThawedHandler.php
Normal file
108
Classes/Typo3/Extbase/DataMapping/AfterObjectThawedHandler.php
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WerkraumMedia\ThueCat\Typo3\Extbase\DataMapping;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Resource\FileRepository;
|
||||||
|
use TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent;
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory;
|
||||||
|
use WerkraumMedia\ThueCat\Domain\Model\Frontend\Base;
|
||||||
|
use WerkraumMedia\ThueCat\Domain\Model\Frontend\Media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will extend mapped objects with further info.
|
||||||
|
*
|
||||||
|
* E.g. will add editorial images to media property.
|
||||||
|
*/
|
||||||
|
class AfterObjectThawedHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FileRepository
|
||||||
|
*/
|
||||||
|
private $fileRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DataMapFactory
|
||||||
|
*/
|
||||||
|
private $dataMapFactory;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
FileRepository $fileRepository,
|
||||||
|
DataMapFactory $dataMapFactory
|
||||||
|
) {
|
||||||
|
$this->fileRepository = $fileRepository;
|
||||||
|
$this->dataMapFactory = $dataMapFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(AfterObjectThawedEvent $event): void
|
||||||
|
{
|
||||||
|
$object = $event->getObject();
|
||||||
|
$record = $event->getRecord();
|
||||||
|
|
||||||
|
if (
|
||||||
|
$object instanceof Base
|
||||||
|
&& ($record['editorial_images'] ?? 0) > 0
|
||||||
|
) {
|
||||||
|
$this->attachEditorialImages($object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function attachEditorialImages(Base $object): void
|
||||||
|
{
|
||||||
|
$uid = $object->getUid();
|
||||||
|
if ($uid === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$images = $this->fileRepository->findByRelation(
|
||||||
|
$this->getTableNameForObject($object),
|
||||||
|
'editorial_images',
|
||||||
|
$uid
|
||||||
|
);
|
||||||
|
if ($images === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getMedia($object)->setEditorialImages($images);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTableNameForObject(Base $object): string
|
||||||
|
{
|
||||||
|
return $this->dataMapFactory
|
||||||
|
->buildDataMap(get_class($object))
|
||||||
|
->getTableName()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMedia(Base $object): Media
|
||||||
|
{
|
||||||
|
$media = $object->getMedia();
|
||||||
|
|
||||||
|
if (!$media instanceof Media) {
|
||||||
|
$media = new Media('');
|
||||||
|
$object->_setProperty('media', $media);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $media;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,11 @@ services:
|
||||||
class: 'WerkraumMedia\ThueCat\Service\DateBasedFilter\FilterBasedOnTypo3Context'
|
class: 'WerkraumMedia\ThueCat\Service\DateBasedFilter\FilterBasedOnTypo3Context'
|
||||||
public: true
|
public: true
|
||||||
|
|
||||||
|
WerkraumMedia\ThueCat\Typo3\Extbase\DataMapping\AfterObjectThawedHandler:
|
||||||
|
tags:
|
||||||
|
- name: 'event.listener'
|
||||||
|
event: 'TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent'
|
||||||
|
|
||||||
'cache.thuecat_fetchdata':
|
'cache.thuecat_fetchdata':
|
||||||
class: 'TYPO3\CMS\Core\Cache\Frontend\FrontendInterface'
|
class: 'TYPO3\CMS\Core\Cache\Frontend\FrontendInterface'
|
||||||
factory:
|
factory:
|
||||||
|
|
|
@ -310,6 +310,16 @@ return (static function (string $extensionKey, string $tableName) {
|
||||||
'readOnly' => true,
|
'readOnly' => true,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'editorial_images' => [
|
||||||
|
'label' => $languagePath . '.editorial_images',
|
||||||
|
'l10n_mode' => 'exclude',
|
||||||
|
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
|
||||||
|
'editorial_images',
|
||||||
|
[],
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'palettes' => [
|
'palettes' => [
|
||||||
'language' => [
|
'language' => [
|
||||||
|
@ -319,7 +329,7 @@ return (static function (string $extensionKey, string $tableName) {
|
||||||
],
|
],
|
||||||
'types' => [
|
'types' => [
|
||||||
'0' => [
|
'0' => [
|
||||||
'showitem' => '--palette--;;language, title, description, slogan, start_of_construction, sanitation, other_service, museum_service, architectural_style, traffic_infrastructure, payment_accepted, digital_offer, photography, pets_allowed, is_accessible_for_free, public_access, available_languages, distance_to_public_transport, opening_hours, special_opening_hours, offers, accessibility_specification, address, url, media, remote_id, --div--;' . $languagePath . '.tab.relations, town, managed_by, parking_facility_near_by',
|
'showitem' => '--palette--;;language, title, description, slogan, start_of_construction, sanitation, other_service, museum_service, architectural_style, traffic_infrastructure, payment_accepted, digital_offer, photography, pets_allowed, is_accessible_for_free, public_access, available_languages, distance_to_public_transport, opening_hours, special_opening_hours, offers, accessibility_specification, address, url, media, remote_id, --div--;' . $languagePath . '.tab.relations, town, managed_by, parking_facility_near_by, --div--;' . $languagePath . '.tab.editorial_additions, editorial_images',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -14,6 +14,15 @@ Features
|
||||||
This finally allows to regularly execute imports.
|
This finally allows to regularly execute imports.
|
||||||
This also allows to import from CLI context with differently configured timeouts.
|
This also allows to import from CLI context with differently configured timeouts.
|
||||||
|
|
||||||
|
* Add support for additional images added via TYPO3.
|
||||||
|
Some installations might need to add further images to records imported from ThüCAT.
|
||||||
|
The records are now extended to support adding images by editors.
|
||||||
|
The images are not touched during import.
|
||||||
|
The images are also ignored during clean ups, the editor is in full control.
|
||||||
|
|
||||||
|
This feature for now is only added to tourist attractions by default.
|
||||||
|
The feature is implemented in a way that all objects extending the ``WerkraumMedia\ThueCat\Domain\Model\Frontend\Base`` class are usable by adding an ``editorial_images`` field to their table.
|
||||||
|
|
||||||
Fixes
|
Fixes
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -28,6 +37,11 @@ Tasks
|
||||||
As this might hint at an issue but most probably is okay, e.g. due to none active
|
As this might hint at an issue but most probably is okay, e.g. due to none active
|
||||||
language, missing name, etc.
|
language, missing name, etc.
|
||||||
|
|
||||||
|
* Separate default templates from templates for testing.
|
||||||
|
That way we no longer test the delivered templates, but they should not be used anyway.
|
||||||
|
Also we can now use templates only for testing to ensure that frontend rendering works as expected, without worrying about sites using the templates.
|
||||||
|
The templates were copied and extended for editorial images.
|
||||||
|
|
||||||
Deprecation
|
Deprecation
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,9 @@
|
||||||
<trans-unit id="tx_thuecat_tourist_attraction.tab.relations" xml:space="preserve">
|
<trans-unit id="tx_thuecat_tourist_attraction.tab.relations" xml:space="preserve">
|
||||||
<source>Relations</source>
|
<source>Relations</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="tx_thuecat_tourist_attraction.tab.editorial_additions" xml:space="preserve">
|
||||||
|
<source>Editorial Additions</source>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="tx_thuecat_tourist_attraction.title" xml:space="preserve">
|
<trans-unit id="tx_thuecat_tourist_attraction.title" xml:space="preserve">
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -171,6 +174,9 @@
|
||||||
<trans-unit id="tx_thuecat_tourist_attraction.managed_by.unkown" xml:space="preserve">
|
<trans-unit id="tx_thuecat_tourist_attraction.managed_by.unkown" xml:space="preserve">
|
||||||
<source>Unkown</source>
|
<source>Unkown</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="tx_thuecat_tourist_attraction.editorial_images" xml:space="preserve">
|
||||||
|
<source>Images</source>
|
||||||
|
</trans-unit>
|
||||||
|
|
||||||
<trans-unit id="tx_thuecat_parking_facility" xml:space="preserve">
|
<trans-unit id="tx_thuecat_parking_facility" xml:space="preserve">
|
||||||
<source>Parking Facility</source>
|
<source>Parking Facility</source>
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
page = PAGE
|
page = PAGE
|
||||||
page.10 < styles.content.get
|
page.10 < styles.content.get
|
||||||
|
|
||||||
|
lib.thuecatContentElement {
|
||||||
|
partialRootPaths {
|
||||||
|
0 = EXT:thuecat/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/
|
||||||
|
}
|
||||||
|
templateRootPaths {
|
||||||
|
0 = EXT:thuecat/Tests/Functional/Fixtures/Frontend/Resources/Private/Templates/ContentElement/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="accessibility">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<f:if condition="{specification.searchCriteria}">
|
||||||
|
<ul>
|
||||||
|
<f:for each="{specification.searchCriteria}" key="id" as="criterias">
|
||||||
|
<f:for each="{criterias}" as="criteria">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.searchCriteria.criteria.{criteria}', default: criteria, extensionName: 'Thuecat')}</li>
|
||||||
|
</f:for>
|
||||||
|
</f:for>
|
||||||
|
</ul>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<ul>
|
||||||
|
<f:if condition="{specification.certificationDeaf}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationDeaf}', default: specification.certificationDeaf, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.deaf', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationMental}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationMental}', default: specification.certificationMental, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.mental', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationPartiallyDeaf}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationPartiallyDeaf}', default: specification.certificationPartiallyDeaf, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.partiallyDeaf', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationPartiallyVisual}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationPartiallyVisual}', default: specification.certificationPartiallyVisual, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.partiallyVisual', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationVisual}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationVisual}', default: specification.certificationVisual, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.visual', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationWalking}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationWalking}', default: specification.certificationWalking, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.walking', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{specification.certificationWheelchair}">
|
||||||
|
<li>{f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationWheelchair}', default: specification.certificationWheelchair, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.wheelchair', extensionName: 'Thuecat')}</li>
|
||||||
|
</f:if>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row accordion" id="accordionShortDescriptions">
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionAllGenerations}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingAllGenerations">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#shortDescriptionAllGenerations-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionAllGenerations-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionAllGenerations', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionAllGenerations-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionAllGenerations -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionAllergic}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingAllergic">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#shortDescriptionAllergic-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionAllergic-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionAllergic', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionAllergic-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionAllergic -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionDeaf}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingDeaf">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#shortDescriptionDeaf-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionDeaf-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionDeaf', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionDeaf-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionDeaf -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionMental}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingMetal">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" href="#shortDescriptionMental-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionMental-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionMental', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionMental-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionMental -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionVisual}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingVisual">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" href="#shortDescriptionVisual-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionVisual-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionVisual', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionVisual-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionVisual -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<f:if condition="{specification.shortDescriptionWalking}">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header" id="headingVisual">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" href="#shortDescriptionWalking-{uid}" role="button" aria-expanded="false" aria-controls="shortDescriptionWalking-{uid}">
|
||||||
|
{f:translate(id: 'content.accessibilitySpecification.shortDescriptionWalking', extensionName: 'Thuecat')}
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="shortDescriptionWalking-{uid}">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{specification.shortDescriptionWalking -> f:format.nl2br()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="address">
|
||||||
|
<p>
|
||||||
|
<f:if condition="{address.street}">
|
||||||
|
{address.street}<br>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{address.zip} && {address.city}">
|
||||||
|
{address.zip} {address.city}<br>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{address.email}">
|
||||||
|
<f:link.email email="{address.email}">{address.email}</f:link.email><br>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{address.phone}">
|
||||||
|
<a href="tel:{address.phone}">{address.phone}</a><br>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{address.fax}">
|
||||||
|
{address.fax}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{url}">
|
||||||
|
<a href="{url}" referrerpolicy="no-referrer" rel="noreferrer noopener">{f:translate(id: 'content.url', extensionName: 'Thuecat')}</a>
|
||||||
|
</f:if>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="digital">
|
||||||
|
<p>
|
||||||
|
<f:for each="{digitalOffer}" as="offer">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.digitalOffer.{offer}', default: offer, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="languages">
|
||||||
|
<p>
|
||||||
|
<f:for each="{availableLanguages}" as="language">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.availableLanguage.{language}', default: language, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="museum">
|
||||||
|
<p>
|
||||||
|
<f:for each="{museumServices}" as="service">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.museumService.{service}', default: service, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="offers">
|
||||||
|
<f:if condition="{offers -> f:count()} > 1">
|
||||||
|
<f:then>
|
||||||
|
<f:render section="MultiOffers" arguments="{offers: offers, uid: uid}" />
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<f:render section="SingleOffer" arguments="{offers: offers}" />
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<f:section name="MultiOffers">
|
||||||
|
<div class="accordion" id="accordionOfferPrices">
|
||||||
|
<f:for each="{offers}" as="offer" key="key" iteration="iterator">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h3 class="accordion-header">
|
||||||
|
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#offerPriceGroup-{key}-{uid}" role="button" aria-expanded="false" aria-controls="offerGroup-{key}-{uid}">
|
||||||
|
<f:if condition="{offer.title}"><f:then>{offer.title} <span class="ps-1">({f:translate(id: 'content.price.type.{offer.type}', default: offer.type, extensionName: 'Thuecat')})</span></f:then><f:else>Preisgruppe</f:else></f:if>
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<div class="accordion-collapse collapse" id="offerPriceGroup-{key}-{uid}">
|
||||||
|
<div class="accordion-body row">
|
||||||
|
<f:if condition="{offer.description}">
|
||||||
|
<div class="description col-md-4">
|
||||||
|
<p><small>{offer.description}</small></p>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
<div class="prices col-md-8">
|
||||||
|
<div class="row">
|
||||||
|
<f:for each="{offer.prices}" as="price">
|
||||||
|
<div class="price-item col-md-5">
|
||||||
|
<strong>{price.title}</strong>
|
||||||
|
<span>{price.price -> f:format.currency(decimalSeparator: ',', thousandsSeparator: '.', decimals: 2, currencySign: price.currency)}</span>
|
||||||
|
<small>{f:translate(id: 'content.price.rule.{price.rules.0}', default: price.rule, extensionName: 'Thuecat')}</small>
|
||||||
|
<f:if condition="{price.description}">
|
||||||
|
<p><small>{price.description}</small></p>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</f:section>
|
||||||
|
|
||||||
|
<f:section name="SingleOffer">
|
||||||
|
<f:for each="{offers}" as="offer" key="key">
|
||||||
|
<div class="offer-group">
|
||||||
|
<div class="content">
|
||||||
|
<h3>{offer.title} <span class="ps-1">({f:translate(id: 'content.price.type.{offer.type}', default: offer.type, extensionName: 'Thuecat')})</span></h3>
|
||||||
|
<div class="row">
|
||||||
|
<f:if condition="{offer.description}">
|
||||||
|
<div class="description col-md-4">
|
||||||
|
<p><small>{offer.description}</small></p>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
<div class="prices col-md-8">
|
||||||
|
<div class="masonry-prices row">
|
||||||
|
<f:for each="{offer.prices}" as="price">
|
||||||
|
<div class="price-item col-md-5">
|
||||||
|
<strong>{price.title}</strong>
|
||||||
|
<span>{price.price -> f:format.currency(decimalSeparator: ',', thousandsSeparator: '.', decimals: 2, currencySign: price.currency)}</span>
|
||||||
|
<small>{f:translate(id: 'content.price.rule.{price.rules.0}', default: price.rule, extensionName: 'Thuecat')}</small>
|
||||||
|
<f:if condition="{price.description}">
|
||||||
|
<p><small>{price.description}</small></p>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</f:section>
|
||||||
|
</html>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="openingHours row">
|
||||||
|
<f:for each="{openingHours}" as="openingHour">
|
||||||
|
<f:if condition="{openingHours -> f:count()} == 1">
|
||||||
|
<f:then>
|
||||||
|
<div class="col-12">
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<div class="col-md-6">
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
<h3>
|
||||||
|
{openingHour.from -> f:format.date(format: 'd.m.Y')} -
|
||||||
|
{openingHour.through -> f:format.date(format: 'd.m.Y')}
|
||||||
|
</h3>
|
||||||
|
<div>
|
||||||
|
<f:for each="{openingHour.daysOfWeekWithMondayFirstWeekDay}" as="weekday">
|
||||||
|
<div class="day-row">
|
||||||
|
<div class="day"><span>{f:translate(id: 'content.openingHour.weekday.{weekday}', default: weekday, extensionName: 'Thuecat')}</span></div>
|
||||||
|
<div class="time"><span>{openingHour.opens} - {openingHour.closes}</span></div>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="parking">
|
||||||
|
<f:if condition="{parkingFacilitiesNearBy -> f:count()} > 3">
|
||||||
|
<f:then>
|
||||||
|
<div class="row">
|
||||||
|
<f:for each="{parkingFacilitiesNearBy}" as="parkingFacility">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{parkingFacility.title}
|
||||||
|
{f:render(partial: 'Address', arguments: {address: parkingFacility.address})}
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<f:for each="{parkingFacilitiesNearBy}" as="parkingFacility">
|
||||||
|
{parkingFacility.title}
|
||||||
|
{f:render(partial: 'Address', arguments: {address: parkingFacility.address})}
|
||||||
|
</f:for>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="payment">
|
||||||
|
<p>
|
||||||
|
<f:for each="{paymentAccepted}" as="payment">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.paymentAccepted.{payment}', default: payment, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="photography">
|
||||||
|
<p>
|
||||||
|
<f:for each="{photography}" as="photography">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.photography.{photography}', default: photography, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="sanitation">
|
||||||
|
<p>
|
||||||
|
<f:for each="{sanitation}" as="sanitation">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.sanitation.{sanitation}', default: sanitation, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:for each="{otherServices}" as="service">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.otherService.{service}', default: service, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<div class="traffic">
|
||||||
|
<p>
|
||||||
|
<f:for each="{trafficInfrastructures}" as="trafficInfrastructure">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.trafficInfrastructure.{trafficInfrastructure}', default: trafficInfrastructure, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,170 @@
|
||||||
|
<html
|
||||||
|
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
|
||||||
|
data-namespace-typo3-fluid="true"
|
||||||
|
>
|
||||||
|
<div class="thuecat">
|
||||||
|
<f:for each="{entities}" as="entity">
|
||||||
|
<div class="row thuecat__abstract">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<f:if condition="{entity.slogan}"><span class="badge bg-danger">{entity.slogan}</span></f:if>
|
||||||
|
<h1>{entity.title} ({entity.town.title})</h1>
|
||||||
|
{entity.description -> f:format.html()}
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 order-first order-md-last">
|
||||||
|
<f:if condition="{entity.media.mainImage}">
|
||||||
|
<img class="img-fluid" src="{entity.media.mainImage.url}" />
|
||||||
|
<f:if condition="{entity.media.mainImage.author}">
|
||||||
|
<p>ⓒ {entity.media.mainImage.author}</p>
|
||||||
|
</f:if>
|
||||||
|
</f:if>
|
||||||
|
<f:for each="{entity.media.editorialImages}" as="image">
|
||||||
|
<f:image image="{image}" />
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row thuecat__address">
|
||||||
|
<f:if condition="{entity.parkingFacilitiesNearBy -> f:count()} > 3">
|
||||||
|
<f:then>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<f:if condition="{entity.address}">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-info.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.address', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Address', arguments: {address: entity.address, url: entity.url})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.distanceToPublicTransport}">
|
||||||
|
<div class="distance-transport mt-5">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-bus.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.distanceToPublicTransport', extensionName: 'Thuecat')}</h2>
|
||||||
|
<p>
|
||||||
|
{entity.distanceToPublicTransport.value} {f:translate(id: 'content.unit.{entity.distanceToPublicTransport.unit}', default: entity.distanceToPublicTransport.unit, extensionName: 'Thuecat')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 multiple-parking">
|
||||||
|
<f:if condition="{entity.parkingFacilitiesNearBy}">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-parking.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.parkingFacilitiesNearBy', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Parking', arguments: {parkingFacilitiesNearBy: entity.parkingFacilitiesNearBy})}
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<f:if condition="{entity.address}">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-info.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.address', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Address', arguments: {address: entity.address, url: entity.url})}
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<f:if condition="{entity.distanceToPublicTransport}">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-bus.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.distanceToPublicTransport', extensionName: 'Thuecat')}</h2>
|
||||||
|
<p>
|
||||||
|
{entity.distanceToPublicTransport.value} {f:translate(id: 'content.unit.{entity.distanceToPublicTransport.unit}', default: entity.distanceToPublicTransport.unit, extensionName: 'Thuecat')}
|
||||||
|
</p>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<f:if condition="{entity.parkingFacilitiesNearBy}">
|
||||||
|
<img src="{f:uri.resource(path:'Icons/icon-parking.svg', extensionName:'Thuecat')}" width="30" class="svg-icon" alt="Address">
|
||||||
|
<h2>{f:translate(id: 'content.parkingFacilitiesNearBy', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Parking', arguments: {parkingFacilitiesNearBy: entity.parkingFacilitiesNearBy})}
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
<div class="row thuecat__services">
|
||||||
|
<f:if condition="{entity.generalInformation || entity.otherServices || entity.petsAllowed || entity.isAccessibleForFree
|
||||||
|
|| entity.publicAccess || entity.accessibilitySpecification.certificationStatus || entity.museumServices
|
||||||
|
|| entity.digitalOffer || entity.trafficInfrastructures || entity.paymentAccepted || entity.availableLanguages
|
||||||
|
|| entity.sanitation || entity.photography || entity.startOfConstruction || entity.architecturalStyles}"
|
||||||
|
>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2>{f:translate(id: 'content.generalInformation', extensionName: 'Thuecat')}</h2>
|
||||||
|
<p>
|
||||||
|
<f:if condition="{entity.otherServices}">
|
||||||
|
{f:render(partial: 'Service', arguments: {otherServices: entity.otherServices})}
|
||||||
|
</f:if>
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.petsAllowed.{entity.petsAllowed}', default: entity.petsAllowed, extensionName: 'Thuecat')}</span>
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.isAccessibleForFree.{entity.isAccessibleForFree}', default: entity.isAccessibleForFree, extensionName: 'Thuecat')}</span>
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.publicAccess.{entity.publicAccess}', default: entity.publicAccess, extensionName: 'Thuecat')}</span>
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.accessibilitySpecification.certificationStatus.{entity.accessibilitySpecification.certificationStatus}', default: entity.accessibilitySpecification.certificationStatus, extensionName: 'Thuecat')}</span>
|
||||||
|
</p>
|
||||||
|
<f:if condition="{entity.museumServices}">
|
||||||
|
<h3>{f:translate(id: 'content.museum', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Museum', arguments: {museumServices: entity.museumServices})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.digitalOffer}">
|
||||||
|
<h3>{f:translate(id: 'content.digital', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Digital', arguments: {digitalOffer: entity.digitalOffer})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.trafficInfrastructures}">
|
||||||
|
<h3>{f:translate(id: 'content.traffic', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Traffic', arguments: {trafficInfrastructures: entity.trafficInfrastructures})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.paymentAccepted}">
|
||||||
|
<h3>{f:translate(id: 'content.payment', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Payment', arguments: {paymentAccepted: entity.paymentAccepted})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.availableLanguages}">
|
||||||
|
<h3>{f:translate(id: 'content.languages', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Payment', arguments: {availableLanguages: entity.availableLanguages})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.sanitation}">
|
||||||
|
<h3>{f:translate(id: 'content.sanitation', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Sanitation', arguments: {sanitation: entity.sanitation})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.photography}">
|
||||||
|
<h3>{f:translate(id: 'content.photography', extensionName: 'Thuecat')}</h3>
|
||||||
|
{f:render(partial: 'Photography', arguments: {photography: entity.photography})}
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.startOfConstruction}">
|
||||||
|
<h3>{f:translate(id: 'content.construction', extensionName: 'Thuecat')}</h3>
|
||||||
|
<p>
|
||||||
|
<span class="badge bg-light">{entity.startOfConstruction}</span>
|
||||||
|
</p>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.architecturalStyles}">
|
||||||
|
<h3>{f:translate(id: 'content.architecture', extensionName: 'Thuecat')}</h3>
|
||||||
|
<p>
|
||||||
|
<f:for each="{entity.architecturalStyles}" as="style">
|
||||||
|
<span class="badge bg-light">{f:translate(id: 'content.architecturalStyle.{style}', default: style, extensionName: 'Thuecat')}</span>
|
||||||
|
</f:for>
|
||||||
|
</p>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.openingHours}">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2>{f:translate(id: 'content.openingHours', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Opening', arguments: {openingHours: entity.openingHours})}
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.specialOpeningHours}">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h2>{f:translate(id: 'content.specialOpeningHours', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Opening', arguments: {openingHours: entity.specialOpeningHours})}
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thuecat__offers">
|
||||||
|
<f:if condition="{entity.offers}">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="text-center">{f:translate(id: 'content.offers', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Offers', arguments: {offers: entity.offers, uid: entity.uid})}
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{entity.accessibilitySpecification.certificationStatus} != ''">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="text-center">{f:translate(id: 'content.accessibility', extensionName: 'Thuecat')}</h2>
|
||||||
|
{f:render(partial: 'Accessibility', arguments: {specification: entity.accessibilitySpecification, uid: entity.uid})}
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<dataset>
|
||||||
|
<tx_thuecat_tourist_attraction>
|
||||||
|
<uid>1</uid>
|
||||||
|
<pid>3</pid>
|
||||||
|
<title>Attraktion mit redaktionellen Bildern</title>
|
||||||
|
<editorial_images>2</editorial_images>
|
||||||
|
</tx_thuecat_tourist_attraction>
|
||||||
|
|
||||||
|
<sys_file_reference>
|
||||||
|
<uid>1</uid>
|
||||||
|
<uid_local>1</uid_local>
|
||||||
|
<uid_foreign>1</uid_foreign>
|
||||||
|
<tablenames>tx_thuecat_tourist_attraction</tablenames>
|
||||||
|
<fieldname>editorial_images</fieldname>
|
||||||
|
<sorting_foreign>1</sorting_foreign>
|
||||||
|
<table_local>sys_file</table_local>
|
||||||
|
</sys_file_reference>
|
||||||
|
|
||||||
|
<sys_file_reference>
|
||||||
|
<uid>2</uid>
|
||||||
|
<uid_local>2</uid_local>
|
||||||
|
<uid_foreign>1</uid_foreign>
|
||||||
|
<tablenames>tx_thuecat_tourist_attraction</tablenames>
|
||||||
|
<fieldname>editorial_images</fieldname>
|
||||||
|
<sorting_foreign>2</sorting_foreign>
|
||||||
|
<table_local>sys_file</table_local>
|
||||||
|
</sys_file_reference>
|
||||||
|
|
||||||
|
<sys_file>
|
||||||
|
<uid>1</uid>
|
||||||
|
<type>2</type>
|
||||||
|
<storage>1</storage>
|
||||||
|
<identifier>/tourismus/images/inhalte/sehenswertes/parks_gaerten/hirschgarten/2998_Spielplaetze_Hirschgarten.jpg</identifier>
|
||||||
|
<extension>jpg</extension>
|
||||||
|
<mime_type>image/jpeg</mime_type>
|
||||||
|
<name>2998_Spielplaetze_Hirschgarten.jpg</name>
|
||||||
|
<sha1>61079cbeb5d13c21d20dbbcc2e28e9c8fa04b3b4</sha1>
|
||||||
|
<size>7329219</size>
|
||||||
|
<identifier_hash>69066cc9c3b5ff135a7daa36059b18c75b3d9a23</identifier_hash>
|
||||||
|
<folder_hash>4dd66a1c0a2a0ab89a22bfe734df75d9750d28f2</folder_hash>
|
||||||
|
</sys_file>
|
||||||
|
|
||||||
|
<sys_file>
|
||||||
|
<uid>2</uid>
|
||||||
|
<type>2</type>
|
||||||
|
<storage>1</storage>
|
||||||
|
<identifier>/tourismus/images/inhalte/sehenswertes/sehenswuerdigkeiten/Petersberg/20_Erfurt-Schriftzug_Petersberg_2021__c_Stadtverwaltung_Erfurt_CC-BY-NC-SA.JPG</identifier>
|
||||||
|
<extension>JPG</extension>
|
||||||
|
<mime_type>image/jpeg</mime_type>
|
||||||
|
<name>20_Erfurt-Schriftzug_Petersberg_2021__c_Stadtverwaltung_Erfurt_CC-BY-NC-SA.JPG</name>
|
||||||
|
<sha1>f4c45d3c738d29162759ecd7d2dbc9af2a8f515f</sha1>
|
||||||
|
<size>2807135</size>
|
||||||
|
<identifier_hash>384f006a1452e901badb0db799fa7ff364e88a5e</identifier_hash>
|
||||||
|
<folder_hash>01086eae3464ef516edc0756ba3e12e35e09c33d</folder_hash>
|
||||||
|
</sys_file>
|
||||||
|
</dataset>
|
||||||
|
|
|
@ -28,27 +28,27 @@ use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
|
||||||
|
|
||||||
class FrontendTest extends FunctionalTestCase
|
class FrontendTest extends FunctionalTestCase
|
||||||
{
|
{
|
||||||
protected $coreExtensionsToLoad = [
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->coreExtensionsToLoad = [
|
||||||
'fluid_styled_content',
|
'fluid_styled_content',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $testExtensionsToLoad = [
|
$this->testExtensionsToLoad = [
|
||||||
'typo3conf/ext/thuecat/',
|
'typo3conf/ext/thuecat/',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $pathsToLinkInTestInstance = [
|
$this->pathsToLinkInTestInstance = [
|
||||||
'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Frontend/Sites/' => 'typo3conf/sites',
|
'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Frontend/Sites/' => 'typo3conf/sites',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->importDataSet('EXT:thuecat/Tests/Functional/Fixtures/Frontend/Content.xml');
|
$this->importDataSet('EXT:thuecat/Tests/Functional/Fixtures/Frontend/Content.xml');
|
||||||
$this->setUpFrontendRootPage(1, [
|
$this->setUpFrontendRootPage(1, [
|
||||||
'EXT:thuecat/Tests/Functional/Fixtures/Frontend/Rendering.typoscript',
|
|
||||||
'EXT:fluid_styled_content/Configuration/TypoScript/setup.typoscript',
|
'EXT:fluid_styled_content/Configuration/TypoScript/setup.typoscript',
|
||||||
'EXT:thuecat/Configuration/TypoScript/ContentElements/setup.typoscript',
|
'EXT:thuecat/Configuration/TypoScript/ContentElements/setup.typoscript',
|
||||||
|
'EXT:thuecat/Tests/Functional/Fixtures/Frontend/Rendering.typoscript',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -690,4 +690,26 @@ class FrontendTest extends FunctionalTestCase
|
||||||
|
|
||||||
self::assertLessThan($positionSecondHour, $positionFirstHour, 'Second hour does not come after first hour.');
|
self::assertLessThan($positionSecondHour, $positionFirstHour, 'Second hour does not come after first hour.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function editorialImagesOfTouristAttractionAreRenderedForDefaultLanguage(): void
|
||||||
|
{
|
||||||
|
$this->importDataSet('EXT:thuecat/Tests/Functional/Fixtures/Frontend/TouristAttractionWithEditorialImages.xml');
|
||||||
|
|
||||||
|
$request = new InternalRequest();
|
||||||
|
$request = $request->withPageId(2);
|
||||||
|
|
||||||
|
$html = (string)$this->executeFrontendRequest($request)->getBody();
|
||||||
|
|
||||||
|
self::assertStringContainsString(
|
||||||
|
'<img src="/fileadmin/tourismus/images/inhalte/sehenswertes/parks_gaerten/hirschgarten/2998_Spielplaetze_Hirschgarten.jpg" width="" height="" alt="" />',
|
||||||
|
$html
|
||||||
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
'<img src="/fileadmin/tourismus/images/inhalte/sehenswertes/sehenswuerdigkeiten/Petersberg/20_Erfurt-Schriftzug_Petersberg_2021__c_Stadtverwaltung_Erfurt_CC-BY-NC-SA.JPG" width="" height="" alt="" />',
|
||||||
|
$html,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace WerkraumMedia\ThueCat\Tests\Unit\Domain\Model\Frontend;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use TYPO3\CMS\Core\Resource\FileReference;
|
||||||
use WerkraumMedia\ThueCat\Domain\Model\Frontend\Media;
|
use WerkraumMedia\ThueCat\Domain\Model\Frontend\Media;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,4 +230,98 @@ class MediaTest extends TestCase
|
||||||
$subject->getExtraImages()[1]['copyrightAuthor']
|
$subject->getExtraImages()[1]['copyrightAuthor']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function returnsEmptyArrayAsDefaultForEditorialImages(): void
|
||||||
|
{
|
||||||
|
$subject = new Media('');
|
||||||
|
self::assertSame(
|
||||||
|
[],
|
||||||
|
$subject->getEditorialImages()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function returnsSetEditorialImages(): void
|
||||||
|
{
|
||||||
|
$subject = new Media('');
|
||||||
|
$reference1 = $this->createStub(FileReference::class);
|
||||||
|
$reference2 = $this->createStub(FileReference::class);
|
||||||
|
$subject->setEditorialImages([
|
||||||
|
$reference1,
|
||||||
|
$reference2,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$images = $subject->getEditorialImages();
|
||||||
|
|
||||||
|
self::assertCount(2, $images);
|
||||||
|
self::assertSame($reference1, $images[0]);
|
||||||
|
self::assertSame($reference2, $images[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function returnsEmptyArrayAsDefaultForAllImages(): void
|
||||||
|
{
|
||||||
|
$subject = new Media('');
|
||||||
|
self::assertSame(
|
||||||
|
[],
|
||||||
|
$subject->getAllImages()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function returnsAllImages(): void
|
||||||
|
{
|
||||||
|
$subject = new Media(json_encode([
|
||||||
|
[
|
||||||
|
'mainImage' => false,
|
||||||
|
'type' => 'image',
|
||||||
|
'title' => 'Erfurt-Dom-und-Severikirche.jpg',
|
||||||
|
'author' => 'Full Name 1',
|
||||||
|
'license' => [
|
||||||
|
'author' => 'Full Name 1 license',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mainImage' => false,
|
||||||
|
'type' => 'image',
|
||||||
|
'title' => 'Erfurt-Dom und Severikirche-beleuchtet.jpg',
|
||||||
|
'author' => 'Full Name 2',
|
||||||
|
'license' => [
|
||||||
|
'author' => 'Full Name 2 license',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]) ?: '');
|
||||||
|
$reference1 = $this->createStub(FileReference::class);
|
||||||
|
$reference2 = $this->createStub(FileReference::class);
|
||||||
|
$subject->setEditorialImages([
|
||||||
|
$reference1,
|
||||||
|
$reference2,
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
$reference1,
|
||||||
|
$subject->getAllImages()[0]
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
$reference2,
|
||||||
|
$subject->getAllImages()[1]
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
'Full Name 1',
|
||||||
|
$subject->getAllImages()[2]['copyrightAuthor']
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
'Full Name 2',
|
||||||
|
$subject->getAllImages()[3]['copyrightAuthor']
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ CREATE TABLE tx_thuecat_tourist_attraction (
|
||||||
available_languages text,
|
available_languages text,
|
||||||
distance_to_public_transport text,
|
distance_to_public_transport text,
|
||||||
accessibility_specification text,
|
accessibility_specification text,
|
||||||
|
editorial_images int(11) unsigned DEFAULT '0' NOT NULL,
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE tx_thuecat_parking_facility (
|
CREATE TABLE tx_thuecat_parking_facility (
|
||||||
|
|
Loading…
Reference in a new issue