diff --git a/Classes/Domain/Model/Frontend/Media.php b/Classes/Domain/Model/Frontend/Media.php index de7bba9..028e6c2 100644 --- a/Classes/Domain/Model/Frontend/Media.php +++ b/Classes/Domain/Model/Frontend/Media.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace WerkraumMedia\ThueCat\Domain\Model\Frontend; +use TYPO3\CMS\Core\Resource\FileReference; use TYPO3\CMS\Core\Type\TypeInterface; class Media implements TypeInterface @@ -37,6 +38,11 @@ class Media implements TypeInterface */ private $data; + /** + * @var FileReference[] + */ + protected $editorialImages = []; + public function __construct(string $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 { return $this->serialized; diff --git a/Classes/Typo3/Extbase/DataMapping/AfterObjectThawedHandler.php b/Classes/Typo3/Extbase/DataMapping/AfterObjectThawedHandler.php new file mode 100644 index 0000000..e977729 --- /dev/null +++ b/Classes/Typo3/Extbase/DataMapping/AfterObjectThawedHandler.php @@ -0,0 +1,108 @@ + + * + * 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; + } +} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index c7357f3..571b69a 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -25,6 +25,11 @@ services: class: 'WerkraumMedia\ThueCat\Service\DateBasedFilter\FilterBasedOnTypo3Context' public: true + WerkraumMedia\ThueCat\Typo3\Extbase\DataMapping\AfterObjectThawedHandler: + tags: + - name: 'event.listener' + event: 'TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent' + 'cache.thuecat_fetchdata': class: 'TYPO3\CMS\Core\Cache\Frontend\FrontendInterface' factory: diff --git a/Configuration/TCA/tx_thuecat_tourist_attraction.php b/Configuration/TCA/tx_thuecat_tourist_attraction.php index 49222e9..dd2cf80 100644 --- a/Configuration/TCA/tx_thuecat_tourist_attraction.php +++ b/Configuration/TCA/tx_thuecat_tourist_attraction.php @@ -310,6 +310,16 @@ return (static function (string $extensionKey, string $tableName) { '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' => [ 'language' => [ @@ -319,7 +329,7 @@ return (static function (string $extensionKey, string $tableName) { ], 'types' => [ '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', ], ], ]; diff --git a/Documentation/Changelog/2.1.0.rst b/Documentation/Changelog/2.1.0.rst index c050ced..94af50f 100644 --- a/Documentation/Changelog/2.1.0.rst +++ b/Documentation/Changelog/2.1.0.rst @@ -14,6 +14,15 @@ Features This finally allows to regularly execute imports. 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 ----- @@ -28,6 +37,11 @@ Tasks As this might hint at an issue but most probably is okay, e.g. due to none active 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 ----------- diff --git a/Resources/Private/Language/locallang_tca.xlf b/Resources/Private/Language/locallang_tca.xlf index 51052fe..8a9bca3 100644 --- a/Resources/Private/Language/locallang_tca.xlf +++ b/Resources/Private/Language/locallang_tca.xlf @@ -84,6 +84,9 @@ Relations + + Editorial Additions + Title @@ -171,6 +174,9 @@ Unkown + + Images + Parking Facility diff --git a/Tests/Functional/Fixtures/Frontend/Rendering.typoscript b/Tests/Functional/Fixtures/Frontend/Rendering.typoscript index 9280c5b..7ab0186 100644 --- a/Tests/Functional/Fixtures/Frontend/Rendering.typoscript +++ b/Tests/Functional/Fixtures/Frontend/Rendering.typoscript @@ -1,2 +1,11 @@ page = PAGE 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/ + } +} diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Accessibility.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Accessibility.html new file mode 100644 index 0000000..f7f294f --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Accessibility.html @@ -0,0 +1,136 @@ + +
+
+
+ +
    + + +
  • {f:translate(id: 'content.accessibilitySpecification.searchCriteria.criteria.{criteria}', default: criteria, extensionName: 'Thuecat')}
  • +
    +
    +
+
+
+
+
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationDeaf}', default: specification.certificationDeaf, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.deaf', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationMental}', default: specification.certificationMental, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.mental', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationPartiallyDeaf}', default: specification.certificationPartiallyDeaf, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.partiallyDeaf', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationPartiallyVisual}', default: specification.certificationPartiallyVisual, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.partiallyVisual', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationVisual}', default: specification.certificationVisual, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.visual', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationWalking}', default: specification.certificationWalking, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.walking', extensionName: 'Thuecat')}
  • +
    + +
  • {f:translate(id: 'content.accessibilitySpecification.certification.{specification.certificationWheelchair}', default: specification.certificationWheelchair, extensionName: 'Thuecat')} {f:translate(id: 'content.accessibilitySpecification.certification.wheelchair', extensionName: 'Thuecat')}
  • +
    +
+
+
+ +
+ + +
+

+ +

+
+
+

{specification.shortDescriptionAllGenerations -> f:format.nl2br()}

+
+
+
+
+ + +
+

+ +

+
+
+

{specification.shortDescriptionAllergic -> f:format.nl2br()}

+
+
+
+
+ + +
+

+ +

+
+
+

{specification.shortDescriptionDeaf -> f:format.nl2br()}

+
+
+
+
+ + +
+

+ +

+
+
+

{specification.shortDescriptionMental -> f:format.nl2br()}

+
+
+
+
+ + +
+

+ +

+
+
+

{specification.shortDescriptionVisual -> f:format.nl2br()}

+
+
+
+
+ + +
+

+ +

+
+
+

{specification.shortDescriptionWalking -> f:format.nl2br()}

+
+
+
+
+
+ +
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Address.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Address.html new file mode 100644 index 0000000..2c726b8 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Address.html @@ -0,0 +1,24 @@ + +
+

+ + {address.street}
+
+ + {address.zip} {address.city}
+
+ + {address.email}
+
+ + {address.phone}
+
+ + {address.fax} + + + {f:translate(id: 'content.url', extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Digital.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Digital.html new file mode 100644 index 0000000..33df985 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Digital.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.digitalOffer.{offer}', default: offer, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Languages.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Languages.html new file mode 100644 index 0000000..8c47cd4 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Languages.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.availableLanguage.{language}', default: language, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Museum.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Museum.html new file mode 100644 index 0000000..61f79d0 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Museum.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.museumService.{service}', default: service, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Offers.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Offers.html new file mode 100644 index 0000000..445ba6b --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Offers.html @@ -0,0 +1,80 @@ + +
+ + + + + + + + +
+ + +
+ +
+

+ +

+
+
+ +
+

{offer.description}

+
+
+
+
+ +
+ {price.title} + {price.price -> f:format.currency(decimalSeparator: ',', thousandsSeparator: '.', decimals: 2, currencySign: price.currency)} + {f:translate(id: 'content.price.rule.{price.rules.0}', default: price.rule, extensionName: 'Thuecat')} + +

{price.description}

+
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+

{offer.title} ({f:translate(id: 'content.price.type.{offer.type}', default: offer.type, extensionName: 'Thuecat')})

+
+ +
+

{offer.description}

+
+
+
+
+ +
+ {price.title} + {price.price -> f:format.currency(decimalSeparator: ',', thousandsSeparator: '.', decimals: 2, currencySign: price.currency)} + {f:translate(id: 'content.price.rule.{price.rules.0}', default: price.rule, extensionName: 'Thuecat')} + +

{price.description}

+
+
+
+
+
+
+
+
+
+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Opening.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Opening.html new file mode 100644 index 0000000..469dcdb --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Opening.html @@ -0,0 +1,27 @@ + +
+ + + +
+ + +
+ + +

+ {openingHour.from -> f:format.date(format: 'd.m.Y')} - + {openingHour.through -> f:format.date(format: 'd.m.Y')} +

+
+ +
+
{f:translate(id: 'content.openingHour.weekday.{weekday}', default: weekday, extensionName: 'Thuecat')}
+
{openingHour.opens} - {openingHour.closes}
+
+
+
+
+ +
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Parking.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Parking.html new file mode 100644 index 0000000..6a2802a --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Parking.html @@ -0,0 +1,22 @@ + +
+ + +
+ +
+ {parkingFacility.title} + {f:render(partial: 'Address', arguments: {address: parkingFacility.address})} +
+
+
+
+ + + {parkingFacility.title} + {f:render(partial: 'Address', arguments: {address: parkingFacility.address})} + + +
+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Payment.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Payment.html new file mode 100644 index 0000000..9a7589f --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Payment.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.paymentAccepted.{payment}', default: payment, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Photography.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Photography.html new file mode 100644 index 0000000..b76f987 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Photography.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.photography.{photography}', default: photography, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Sanitation.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Sanitation.html new file mode 100644 index 0000000..49167b3 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Sanitation.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.sanitation.{sanitation}', default: sanitation, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Service.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Service.html new file mode 100644 index 0000000..8ac667a --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Service.html @@ -0,0 +1,5 @@ + + + {f:translate(id: 'content.otherService.{service}', default: service, extensionName: 'Thuecat')} + + diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Traffic.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Traffic.html new file mode 100644 index 0000000..0939e88 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Partials/ContentElement/Traffic.html @@ -0,0 +1,9 @@ + +
+

+ + {f:translate(id: 'content.trafficInfrastructure.{trafficInfrastructure}', default: trafficInfrastructure, extensionName: 'Thuecat')} + +

+
+ diff --git a/Tests/Functional/Fixtures/Frontend/Resources/Private/Templates/ContentElement/TouristAttraction.html b/Tests/Functional/Fixtures/Frontend/Resources/Private/Templates/ContentElement/TouristAttraction.html new file mode 100644 index 0000000..3e89356 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/Resources/Private/Templates/ContentElement/TouristAttraction.html @@ -0,0 +1,170 @@ + +
+ +
+
+ {entity.slogan} +

{entity.title} ({entity.town.title})

+ {entity.description -> f:format.html()} +
+
+ + + +

ⓒ {entity.media.mainImage.author}

+
+
+ + + +
+
+
+ + +
+ + Address +

{f:translate(id: 'content.address', extensionName: 'Thuecat')}

+ {f:render(partial: 'Address', arguments: {address: entity.address, url: entity.url})} +
+ +
+ Address +

{f:translate(id: 'content.distanceToPublicTransport', extensionName: 'Thuecat')}

+

+ {entity.distanceToPublicTransport.value} {f:translate(id: 'content.unit.{entity.distanceToPublicTransport.unit}', default: entity.distanceToPublicTransport.unit, extensionName: 'Thuecat')} +

+
+
+
+
+ + Address +

{f:translate(id: 'content.parkingFacilitiesNearBy', extensionName: 'Thuecat')}

+ {f:render(partial: 'Parking', arguments: {parkingFacilitiesNearBy: entity.parkingFacilitiesNearBy})} +
+
+
+ +
+ + Address +

{f:translate(id: 'content.address', extensionName: 'Thuecat')}

+ {f:render(partial: 'Address', arguments: {address: entity.address, url: entity.url})} +
+
+
+ + Address +

{f:translate(id: 'content.distanceToPublicTransport', extensionName: 'Thuecat')}

+

+ {entity.distanceToPublicTransport.value} {f:translate(id: 'content.unit.{entity.distanceToPublicTransport.unit}', default: entity.distanceToPublicTransport.unit, extensionName: 'Thuecat')} +

+
+
+
+ + Address +

{f:translate(id: 'content.parkingFacilitiesNearBy', extensionName: 'Thuecat')}

+ {f:render(partial: 'Parking', arguments: {parkingFacilitiesNearBy: entity.parkingFacilitiesNearBy})} +
+
+
+
+
+
+ +
+

{f:translate(id: 'content.generalInformation', extensionName: 'Thuecat')}

+

+ + {f:render(partial: 'Service', arguments: {otherServices: entity.otherServices})} + + {f:translate(id: 'content.petsAllowed.{entity.petsAllowed}', default: entity.petsAllowed, extensionName: 'Thuecat')} + {f:translate(id: 'content.isAccessibleForFree.{entity.isAccessibleForFree}', default: entity.isAccessibleForFree, extensionName: 'Thuecat')} + {f:translate(id: 'content.publicAccess.{entity.publicAccess}', default: entity.publicAccess, extensionName: 'Thuecat')} + {f:translate(id: 'content.accessibilitySpecification.certificationStatus.{entity.accessibilitySpecification.certificationStatus}', default: entity.accessibilitySpecification.certificationStatus, extensionName: 'Thuecat')} +

+ +

{f:translate(id: 'content.museum', extensionName: 'Thuecat')}

+ {f:render(partial: 'Museum', arguments: {museumServices: entity.museumServices})} +
+ +

{f:translate(id: 'content.digital', extensionName: 'Thuecat')}

+ {f:render(partial: 'Digital', arguments: {digitalOffer: entity.digitalOffer})} +
+ +

{f:translate(id: 'content.traffic', extensionName: 'Thuecat')}

+ {f:render(partial: 'Traffic', arguments: {trafficInfrastructures: entity.trafficInfrastructures})} +
+ +

{f:translate(id: 'content.payment', extensionName: 'Thuecat')}

+ {f:render(partial: 'Payment', arguments: {paymentAccepted: entity.paymentAccepted})} +
+ +

{f:translate(id: 'content.languages', extensionName: 'Thuecat')}

+ {f:render(partial: 'Payment', arguments: {availableLanguages: entity.availableLanguages})} +
+ +

{f:translate(id: 'content.sanitation', extensionName: 'Thuecat')}

+ {f:render(partial: 'Sanitation', arguments: {sanitation: entity.sanitation})} +
+ +

{f:translate(id: 'content.photography', extensionName: 'Thuecat')}

+ {f:render(partial: 'Photography', arguments: {photography: entity.photography})} +
+ +

{f:translate(id: 'content.construction', extensionName: 'Thuecat')}

+

+ {entity.startOfConstruction} +

+
+ +

{f:translate(id: 'content.architecture', extensionName: 'Thuecat')}

+

+ + {f:translate(id: 'content.architecturalStyle.{style}', default: style, extensionName: 'Thuecat')} + +

+
+
+
+ +
+

{f:translate(id: 'content.openingHours', extensionName: 'Thuecat')}

+ {f:render(partial: 'Opening', arguments: {openingHours: entity.openingHours})} +
+
+ +
+

{f:translate(id: 'content.specialOpeningHours', extensionName: 'Thuecat')}

+ {f:render(partial: 'Opening', arguments: {openingHours: entity.specialOpeningHours})} +
+
+
+ +
+ +
+

{f:translate(id: 'content.offers', extensionName: 'Thuecat')}

+ {f:render(partial: 'Offers', arguments: {offers: entity.offers, uid: entity.uid})} +
+
+ +
+

{f:translate(id: 'content.accessibility', extensionName: 'Thuecat')}

+ {f:render(partial: 'Accessibility', arguments: {specification: entity.accessibilitySpecification, uid: entity.uid})} +
+
+
+
+
+ diff --git a/Tests/Functional/Fixtures/Frontend/TouristAttractionWithEditorialImages.xml b/Tests/Functional/Fixtures/Frontend/TouristAttractionWithEditorialImages.xml new file mode 100644 index 0000000..d7de1d0 --- /dev/null +++ b/Tests/Functional/Fixtures/Frontend/TouristAttractionWithEditorialImages.xml @@ -0,0 +1,58 @@ + + + + 1 + 3 + Attraktion mit redaktionellen Bildern + 2 + + + + 1 + 1 + 1 + tx_thuecat_tourist_attraction + editorial_images + 1 + sys_file + + + + 2 + 2 + 1 + tx_thuecat_tourist_attraction + editorial_images + 2 + sys_file + + + + 1 + 2 + 1 + /tourismus/images/inhalte/sehenswertes/parks_gaerten/hirschgarten/2998_Spielplaetze_Hirschgarten.jpg + jpg + image/jpeg + 2998_Spielplaetze_Hirschgarten.jpg + 61079cbeb5d13c21d20dbbcc2e28e9c8fa04b3b4 + 7329219 + 69066cc9c3b5ff135a7daa36059b18c75b3d9a23 + 4dd66a1c0a2a0ab89a22bfe734df75d9750d28f2 + + + + 2 + 2 + 1 + /tourismus/images/inhalte/sehenswertes/sehenswuerdigkeiten/Petersberg/20_Erfurt-Schriftzug_Petersberg_2021__c_Stadtverwaltung_Erfurt_CC-BY-NC-SA.JPG + JPG + image/jpeg + 20_Erfurt-Schriftzug_Petersberg_2021__c_Stadtverwaltung_Erfurt_CC-BY-NC-SA.JPG + f4c45d3c738d29162759ecd7d2dbc9af2a8f515f + 2807135 + 384f006a1452e901badb0db799fa7ff364e88a5e + 01086eae3464ef516edc0756ba3e12e35e09c33d + + + diff --git a/Tests/Functional/FrontendTest.php b/Tests/Functional/FrontendTest.php index 7650e01..4d204f4 100644 --- a/Tests/Functional/FrontendTest.php +++ b/Tests/Functional/FrontendTest.php @@ -28,27 +28,27 @@ use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; class FrontendTest extends FunctionalTestCase { - protected $coreExtensionsToLoad = [ - 'fluid_styled_content', - ]; - - protected $testExtensionsToLoad = [ - 'typo3conf/ext/thuecat/', - ]; - - protected $pathsToLinkInTestInstance = [ - 'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Frontend/Sites/' => 'typo3conf/sites', - ]; - protected function setUp(): void { + $this->coreExtensionsToLoad = [ + 'fluid_styled_content', + ]; + + $this->testExtensionsToLoad = [ + 'typo3conf/ext/thuecat/', + ]; + + $this->pathsToLinkInTestInstance = [ + 'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Frontend/Sites/' => 'typo3conf/sites', + ]; + parent::setUp(); $this->importDataSet('EXT:thuecat/Tests/Functional/Fixtures/Frontend/Content.xml'); $this->setUpFrontendRootPage(1, [ - 'EXT:thuecat/Tests/Functional/Fixtures/Frontend/Rendering.typoscript', 'EXT:fluid_styled_content/Configuration/TypoScript/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.'); } + + /** + * @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( + '', + $html + ); + self::assertStringContainsString( + '', + $html, + ); + } } diff --git a/Tests/Unit/Domain/Model/Frontend/MediaTest.php b/Tests/Unit/Domain/Model/Frontend/MediaTest.php index 2e8958c..f41f15d 100644 --- a/Tests/Unit/Domain/Model/Frontend/MediaTest.php +++ b/Tests/Unit/Domain/Model/Frontend/MediaTest.php @@ -24,6 +24,7 @@ namespace WerkraumMedia\ThueCat\Tests\Unit\Domain\Model\Frontend; */ use PHPUnit\Framework\TestCase; +use TYPO3\CMS\Core\Resource\FileReference; use WerkraumMedia\ThueCat\Domain\Model\Frontend\Media; /** @@ -229,4 +230,98 @@ class MediaTest extends TestCase $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'] + ); + } } diff --git a/ext_tables.sql b/ext_tables.sql index 32c1f79..bee11a6 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -78,6 +78,7 @@ CREATE TABLE tx_thuecat_tourist_attraction ( available_languages text, distance_to_public_transport text, accessibility_specification text, + editorial_images int(11) unsigned DEFAULT '0' NOT NULL, ); CREATE TABLE tx_thuecat_parking_facility (