Support TYPO3 v12 and PHP 8.3

Use rector, PHPUnit, Codeception and PHPStan to find and fix issues.
This commit is contained in:
Daniel Siepmann 2023-11-30 13:52:23 +01:00
parent 8a2c543ab2
commit d39053b498
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
201 changed files with 4383 additions and 3663 deletions

20
.gitattributes vendored
View file

@ -1,12 +1,10 @@
Tests export-ignore
shell.nix export-ignore
.github export-ignore
.gitattributes export-ignore
.gitignore export-ignore
ecs.php export-ignore
phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
phpunit.xml.dist export-ignore
codeception.dist.yml export-ignore
.gitattributes export-ignore
.github export-ignore
.gitignore export-ignore
.php-cs-fixer.dist.php export-ignore
phpstan-baseline.neon export-ignore
phpstan.neon export-ignore
phpunit.xml.dist export-ignore
shell.nix export-ignore
Tests export-ignore

View file

@ -15,10 +15,9 @@ jobs:
strategy:
matrix:
php-version:
- 7.4
- 8.0
- 8.1
- 8.2
- 8.3
steps:
- name: Checkout
uses: actions/checkout@v3
@ -41,7 +40,7 @@ jobs:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: "7.4"
php-version: "8.2"
tools: composer:v2
- name: Install xmllint
@ -70,14 +69,14 @@ jobs:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: "7.4"
php-version: "8.2"
tools: composer:v2
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Coding Guideline
run: ./vendor/bin/ecs check
run: ./vendor/bin/php-cs-fixer fix --dry-run --diff
code-quality:
runs-on: ubuntu-latest
@ -86,16 +85,12 @@ jobs:
strategy:
matrix:
include:
- php-version: '7.4'
typo3-version: '^10.4'
- php-version: '7.4'
typo3-version: '^11.5'
- php-version: '8.0'
typo3-version: '^11.5'
- php-version: '8.1'
typo3-version: '^11.5'
typo3-version: '^12.4'
- php-version: '8.2'
typo3-version: '^11.5'
typo3-version: '^12.4'
- php-version: '8.3'
typo3-version: '^12.4'
steps:
- uses: actions/checkout@v3
@ -121,23 +116,14 @@ jobs:
strategy:
matrix:
include:
- php-version: '7.4'
typo3-version: '^10.4'
db-version: '5.6'
- php-version: '7.4'
typo3-version: '^10.4'
db-version: '8'
- php-version: '7.4'
typo3-version: '^11.5'
db-version: '8'
- php-version: '8.0'
typo3-version: '^11.5'
db-version: '8'
- php-version: '8.1'
typo3-version: '^11.5'
typo3-version: '^12.4'
db-version: '8'
- php-version: '8.2'
typo3-version: '^11.5'
typo3-version: '^12.4'
db-version: '8'
- php-version: '8.3'
typo3-version: '^12.4'
db-version: '8'
steps:
- uses: actions/checkout@v3
@ -151,10 +137,16 @@ jobs:
- name: Setup MySQL
uses: mirromutth/mysql-action@v1.1
with:
mysql version: '5.7'
mysql version: "${{ matrix.db-version }}"
mysql database: 'typo3'
mysql root password: 'root'
- name: Wait for MySQL
run: |
while ! mysqladmin ping --host=127.0.0.1 --password=root --silent; do
sleep 1
done
- name: Install dependencies
run: composer require --no-interaction --prefer-dist --no-progress "typo3/cms-backend:${{ matrix.typo3-version }}" "typo3/cms-core:${{ matrix.typo3-version }}" "typo3/cms-extbase:${{ matrix.typo3-version }}" "typo3/cms-frontend:${{ matrix.typo3-version }}" "typo3/cms-fluid-styled-content:${{ matrix.typo3-version }}"
@ -165,4 +157,16 @@ jobs:
export typo3DatabaseHost="127.0.0.1"
export typo3DatabaseUsername="root"
export typo3DatabasePassword="root"
./vendor/bin/phpunit --testdox
./vendor/bin/phpunit
tests-acceptance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v24
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Run Acceptance Tests
run: nix-shell --run project-test-acceptance

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/.Build/
/.phpunit.cache
/composer.lock
/vendor/

73
.php-cs-fixer.dist.php Normal file
View file

@ -0,0 +1,73 @@
<?php
$finder = (new PhpCsFixer\Finder())
->ignoreVCSIgnored(true)
->in(realpath(__DIR__))
;
return (new \PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@DoctrineAnnotation' => true,
'@PSR2' => true,
'array_indentation' => true,
'array_syntax' => ['syntax' => 'short'],
'attribute_empty_parentheses' => true,
'blank_line_after_opening_tag' => true,
'braces' => ['allow_single_line_closure' => true],
'cast_spaces' => ['space' => 'none'],
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'none'],
'declare_strict_types' => true,
'dir_constant' => true,
'fully_qualified_strict_types' => false,
'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']],
'function_typehint_space' => true,
'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true],
'lowercase_cast' => true,
'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
'modernize_strpos' => true,
'modernize_types_casting' => true,
'multiline_whitespace_before_semicolons' => ['strategy' => 'new_line_for_chained_calls'],
'native_function_casing' => true,
'new_with_braces' => true,
'no_alias_functions' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_extra_blank_lines' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_multiline_whitespace_around_double_arrow' => true,
'no_null_property_initialization' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_superfluous_elseif' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_unneeded_control_parentheses' => true,
'no_unneeded_import_alias' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_whitespace_in_blank_line' => true,
'ordered_imports' => true,
'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']],
'php_unit_mock_short_will_return' => true,
'php_unit_test_case_static_method_calls' => ['call_type' => 'self'],
'phpdoc_no_access' => true,
'phpdoc_no_package' => true,
'phpdoc_order' => ['order' => ['test', 'dataProvider', 'param', 'throws', 'return']],
'phpdoc_scalar' => true,
'phpdoc_separation' => ['groups' => [['Extbase\\*']]],
'phpdoc_trim' => true,
'phpdoc_types' => true,
'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
'return_type_declaration' => ['space_before' => 'none'],
'single_line_comment_style' => ['comment_types' => ['hash']],
'single_quote' => true,
'single_trait_insert_per_statement' => true,
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
'whitespace_after_comma_in_array' => true,
'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false],
])
->setFinder($finder)
;

View file

@ -34,24 +34,11 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepositor
class ImportConfigurationCommand extends Command
{
/**
* @var ImportConfigurationRepository
*/
private $importConfigurationRepository;
/**
* @var Importer
*/
private $importer;
public function __construct(
ImportConfigurationRepository $importConfigurationRepository,
Importer $importer
private readonly ImportConfigurationRepository $importConfigurationRepository,
private readonly Importer $importer
) {
parent::__construct();
$this->importConfigurationRepository = $importConfigurationRepository;
$this->importer = $importer;
}
protected function configure(): void

View file

@ -23,22 +23,30 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Controller\Backend;
use TYPO3\CMS\Backend\View\BackendTemplateView;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
abstract class AbstractController extends ActionController
{
/**
* BackendTemplateContainer
*
* @var BackendTemplateView
*/
protected $view;
private ModuleTemplateFactory $moduleTemplateFactory;
/**
* Backend Template Container
*
* @var string
*/
protected $defaultViewObjectName = BackendTemplateView::class;
protected ModuleTemplate $moduleTemplate;
public function injectModuleTemplateFactory(ModuleTemplateFactory $moduleTemplateFactory): void
{
$this->moduleTemplateFactory = $moduleTemplateFactory;
}
protected function initializeView(): void
{
$this->moduleTemplate = $this->moduleTemplateFactory->create($this->request);
$this->moduleTemplate->assign('settings', $this->settings);
}
protected function htmlResponse(?string $html = null): ResponseInterface
{
return parent::htmlResponse($html ?? $this->moduleTemplate->render());
}
}

View file

@ -23,34 +23,25 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Controller\Backend;
use Psr\Http\Message\ResponseInterface;
use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepository;
use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository;
class ConfigurationController extends AbstractController
{
/**
* @var OrganisationRepository
*/
private $organisationRepository;
/**
* @var ImportConfigurationRepository
*/
private $importConfigurationRepository;
public function __construct(
OrganisationRepository $organisationRepository,
ImportConfigurationRepository $importConfigurationRepository
private readonly OrganisationRepository $organisationRepository,
private readonly ImportConfigurationRepository $importConfigurationRepository
) {
$this->organisationRepository = $organisationRepository;
$this->importConfigurationRepository = $importConfigurationRepository;
}
public function indexAction(): void
public function indexAction(): ResponseInterface
{
$this->view->assignMultiple([
$this->moduleTemplate->assignMultiple([
'importConfigurations' => $this->importConfigurationRepository->findAll(),
'organisations' => $this->organisationRepository->findAll(),
]);
return $this->htmlResponse();
}
}

View file

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Controller\Backend;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
use WerkraumMedia\ThueCat\Domain\Import\Importer;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration;
use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportLogRepository;
@ -33,42 +34,24 @@ use WerkraumMedia\ThueCat\Typo3Wrapper\TranslationService;
class ImportController extends AbstractController
{
/**
* @var Importer
*/
private $importer;
/**
* @var ImportLogRepository
*/
private $repository;
/**
* @var TranslationService
*/
private $translation;
public function __construct(
Importer $importer,
ImportLogRepository $repository,
TranslationService $translation
private readonly Importer $importer,
private readonly ImportLogRepository $repository,
private readonly TranslationService $translation
) {
$this->importer = $importer;
$this->repository = $repository;
$this->translation = $translation;
}
public function indexAction(): void
public function indexAction(): ResponseInterface
{
$this->view->assignMultiple([
$this->moduleTemplate->assignMultiple([
'imports' => $this->repository->findAll(),
]);
return $this->htmlResponse();
}
/**
* @Extbase\IgnoreValidation("importConfiguration")
*/
public function importAction(ImportConfiguration $importConfiguration): void
#[IgnoreValidation(['argumentName' => 'importConfiguration'])]
public function importAction(ImportConfiguration $importConfiguration): ResponseInterface
{
$importLog = $this->importer->importConfiguration($importConfiguration);
@ -78,7 +61,7 @@ class ImportController extends AbstractController
$this->createImportDoneFlashMessage($importConfiguration);
}
$this->redirect('index', 'Backend\Configuration');
return $this->redirect('index', 'Backend\Configuration');
}
private function createImportErrorFlashMessage(ImportConfiguration $importConfiguration): void
@ -93,7 +76,7 @@ class ImportController extends AbstractController
'controller.backend.import.import.error.title',
Extension::EXTENSION_NAME
),
AbstractMessage::ERROR
ContextualFeedbackSeverity::ERROR
);
}
@ -109,7 +92,7 @@ class ImportController extends AbstractController
'controller.backend.import.import.success.title',
Extension::EXTENSION_NAME
),
AbstractMessage::OK
ContextualFeedbackSeverity::OK
);
}
}

View file

@ -109,6 +109,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $accessibilityCertificationStatus
*/
public function setAccessibilityCertificationStatus($accessibilityCertificationStatus): void
@ -121,6 +122,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityDeaf
*/
public function setCertificationAccessibilityDeaf($certificationAccessibilityDeaf): void
@ -133,6 +135,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityMental
*/
public function setCertificationAccessibilityMental($certificationAccessibilityMental): void
@ -145,6 +148,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityPartiallyDeaf
*/
public function setCertificationAccessibilityPartiallyDeaf($certificationAccessibilityPartiallyDeaf): void
@ -157,6 +161,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityPartiallyVisual
*/
public function setCertificationAccessibilityPartiallyVisual($certificationAccessibilityPartiallyVisual): void
@ -169,6 +174,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityVisual
*/
public function setCertificationAccessibilityVisual($certificationAccessibilityVisual): void
@ -181,6 +187,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityWalking
*/
public function setCertificationAccessibilityWalking($certificationAccessibilityWalking): void
@ -193,6 +200,7 @@ class AccessibilityCertification implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $certificationAccessibilityWheelchair
*/
public function setCertificationAccessibilityWheelchair($certificationAccessibilityWheelchair): void

View file

@ -65,6 +65,7 @@ class Base extends Minimum
/**
* @internal for mapping via Symfony component.
*
* @return ForeignReference[]
*/
public function getImage(): array

View file

@ -35,6 +35,7 @@ interface MapsToType
* - thuecat:TouristInformation
* - thuecat:Town
* -
*
* @return string[]
*/
public static function getSupportedTypes(): array;

View file

@ -85,7 +85,7 @@ class MediaObject extends Minimum implements MapsToType
*/
public function setCopyrightYear(string $copyrightYear): void
{
$this->copyrightYear = (int) $copyrightYear;
$this->copyrightYear = (int)$copyrightYear;
}
/**
@ -106,6 +106,7 @@ class MediaObject extends Minimum implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|ForeignReference $author
*/
public function setAuthor($author): void

View file

@ -108,6 +108,7 @@ abstract class Minimum
/**
* @internal for mapping via Symfony component.
*
* @param string|array $url
*/
public function setUrl($url): void

View file

@ -23,14 +23,15 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Entity;
use DateTimeImmutable;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\Address;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\ForeignReference;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\Geo;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\OpeningHour;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Shared\ContainedInPlace;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Shared\Organization;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
use WerkraumMedia\ThueCat\Service\DateBasedFilter;
class Place extends Base
@ -161,10 +162,11 @@ class Place extends Base
return GeneralUtility::makeInstance(DateBasedFilter::class)
->filterOutPreviousDates(
$this->openingHoursSpecifications,
function (OpeningHour $hour): ?\DateTimeImmutable {
function (OpeningHour $hour): ?DateTimeImmutable {
return $hour->getValidThrough();
}
);
)
;
}
/**
@ -175,10 +177,11 @@ class Place extends Base
return GeneralUtility::makeInstance(DateBasedFilter::class)
->filterOutPreviousDates(
$this->specialOpeningHours,
function (OpeningHour $hour): ?\DateTimeImmutable {
function (OpeningHour $hour): ?DateTimeImmutable {
return $hour->getValidThrough();
}
);
)
;
}
/**
@ -252,6 +255,7 @@ class Place extends Base
/**
* @internal for mapping via Symfony component.
*
* @param string|array $sanitation
*/
public function setSanitation($sanitation): void
@ -265,6 +269,7 @@ class Place extends Base
/**
* @internal for mapping via Symfony component.
*
* @param string|array $otherService
*/
public function setOtherService($otherService): void
@ -278,6 +283,7 @@ class Place extends Base
/**
* @internal for mapping via Symfony component.
*
* @param string|array $trafficInfrastructure
*/
public function setTrafficInfrastructure($trafficInfrastructure): void
@ -291,6 +297,7 @@ class Place extends Base
/**
* @internal for mapping via Symfony component.
*
* @param string|array $paymentAccepted
*/
public function setPaymentAccepted($paymentAccepted): void

View file

@ -50,7 +50,7 @@ class Geo
*/
public function setLongitude(string $longitude): void
{
$this->longitude = (float) $longitude;
$this->longitude = (float)$longitude;
}
/**
@ -58,6 +58,6 @@ class Geo
*/
public function setLatitude(string $latitude): void
{
$this->latitude = (float) $latitude;
$this->latitude = (float)$latitude;
}
}

View file

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Entity\Properties;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Minimum;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
class Offer extends Minimum
{
@ -48,6 +48,7 @@ class Offer extends Minimum
/**
* @internal for mapping via Symfony component.
*
* @param string|array $offerType
*/
public function setOfferType($offerType): void
@ -72,6 +73,7 @@ class Offer extends Minimum
/**
* @return PriceSpecification[]
*
* @internal for mapping via Symfony component.
*/
public function getPriceSpecification(): array

View file

@ -23,25 +23,27 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Entity\Properties;
use DateTimeImmutable;
class OpeningHour
{
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
protected $validFrom = null;
protected $validFrom;
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
protected $validThrough = null;
protected $validThrough;
/**
* @var \DateTimeImmutable
* @var DateTimeImmutable
*/
protected $opens;
/**
* @var \DateTimeImmutable
* @var DateTimeImmutable
*/
protected $closes;
@ -50,22 +52,22 @@ class OpeningHour
*/
protected $daysOfWeek = [];
public function getValidFrom(): ?\DateTimeImmutable
public function getValidFrom(): ?DateTimeImmutable
{
return $this->validFrom;
}
public function getValidThrough(): ?\DateTimeImmutable
public function getValidThrough(): ?DateTimeImmutable
{
return $this->validThrough;
}
public function getOpens(): \DateTimeImmutable
public function getOpens(): DateTimeImmutable
{
return $this->opens;
}
public function getCloses(): \DateTimeImmutable
public function getCloses(): DateTimeImmutable
{
return $this->closes;
}
@ -81,7 +83,7 @@ class OpeningHour
/**
* @internal for mapping via Symfony component.
*/
public function setValidFrom(\DateTimeImmutable $validFrom): void
public function setValidFrom(DateTimeImmutable $validFrom): void
{
$this->validFrom = $validFrom;
}
@ -89,7 +91,7 @@ class OpeningHour
/**
* @internal for mapping via Symfony component.
*/
public function setValidThrough(\DateTimeImmutable $validThrough): void
public function setValidThrough(DateTimeImmutable $validThrough): void
{
$this->validThrough = $validThrough;
}
@ -97,7 +99,7 @@ class OpeningHour
/**
* @internal for mapping via Symfony component.
*/
public function setOpens(\DateTimeImmutable $opens): void
public function setOpens(DateTimeImmutable $opens): void
{
$this->opens = $opens;
}
@ -105,7 +107,7 @@ class OpeningHour
/**
* @internal for mapping via Symfony component.
*/
public function setCloses(\DateTimeImmutable $closes): void
public function setCloses(DateTimeImmutable $closes): void
{
$this->closes = $closes;
}

View file

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Entity\Properties;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Minimum;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\PropertyValues;
class PriceSpecification extends Minimum
{
@ -85,6 +85,7 @@ class PriceSpecification extends Minimum
/**
* @internal for mapping via Symfony component.
*
* @param string|array $calculationRule
*/
public function setCalculationRule($calculationRule): void

View file

@ -42,6 +42,7 @@ trait ContainedInPlace
/**
* @return ForeignReference[]
*
* @internal for mapping via Symfony component.
*/
public function getContainedInPlace(): array

View file

@ -46,6 +46,7 @@ trait Organization
/**
* @internal for mapping via Symfony component.
*
* @return Offer[]
*/
public function getMakesOffer(): array

View file

@ -160,6 +160,7 @@ class TouristAttraction extends Place implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $museumService
*/
public function setMuseumService($museumService): void
@ -173,6 +174,7 @@ class TouristAttraction extends Place implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $architecturalStyle
*/
public function setArchitecturalStyle($architecturalStyle): void
@ -186,6 +188,7 @@ class TouristAttraction extends Place implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $digitalOffer
*/
public function setDigitalOffer($digitalOffer): void
@ -199,6 +202,7 @@ class TouristAttraction extends Place implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $photography
*/
public function setPhotography($photography): void
@ -236,6 +240,7 @@ class TouristAttraction extends Place implements MapsToType
/**
* @internal for mapping via Symfony component.
*
* @param string|array $availableLanguage
*/
public function setAvailableLanguage($availableLanguage): void

View file

@ -30,6 +30,7 @@ use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Throwable;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\ArrayDenormalizer;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\CustomAnnotationExtractor;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\JsonDecode;
@ -55,7 +56,7 @@ class EntityMapper
'json',
$context
);
} catch (\Throwable $e) {
} catch (Throwable $e) {
throw new MappingException($jsonLD, $targetClassName, $e);
}
}

View file

@ -23,13 +23,21 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\EntityMapper;
use function in_array;
use InvalidArgumentException;
use LogicException;
use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\ContextFactory;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionProperty;
use RuntimeException;
use Symfony\Component\PropertyInfo\Extractor\ConstructorArgumentTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
@ -75,7 +83,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
public function __construct(DocBlockFactoryInterface $docBlockFactory = null, array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
{
if (!class_exists(DocBlockFactory::class)) {
throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed. Try running composer require "phpdocumentor/reflection-docblock".', __CLASS__));
throw new LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed. Try running composer require "phpdocumentor/reflection-docblock".', __CLASS__));
}
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
@ -129,7 +137,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
$contents = $docBlock->getDescription()->render();
return '' === $contents ? null : $contents;
return $contents === '' ? null : $contents;
}
/**
@ -161,7 +169,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
$types = [];
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName($tag) as $tag) {
if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
if ($tag && !$tag instanceof InvalidTag && $tag->getType() !== null) {
foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
switch ($type->getClassName()) {
case 'self':
@ -189,7 +197,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
return null;
}
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
if (!in_array($prefix, $this->arrayMutatorPrefixes)) {
return $types;
}
@ -210,12 +218,12 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
$types = [];
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName('param') as $tag) {
if ($tag && null !== $tag->getType()) {
if ($tag && $tag->getType() !== null) {
$types[] = $this->phpDocTypeHelper->getTypes($tag->getType());
}
}
if (!isset($types[0]) || [] === $types[0]) {
if (!isset($types[0]) || $types[0] === []) {
return null;
}
@ -225,8 +233,8 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock
{
try {
$reflectionClass = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
$reflectionClass = new ReflectionClass($class);
} catch (ReflectionException $e) {
return null;
}
$reflectionConstructor = $reflectionClass->getConstructor();
@ -238,7 +246,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
$docBlock = $this->docBlockFactory->create($reflectionConstructor, $this->contextFactory->createFromReflector($reflectionConstructor));
return $this->filterDocBlockParams($docBlock, $property);
} catch (\InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
return null;
}
}
@ -246,11 +254,18 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam): DocBlock
{
$tags = array_values(array_filter($docBlock->getTagsByName('param'), function ($tag) use ($allowedParam) {
return $tag instanceof DocBlock\Tags\Param && $allowedParam === $tag->getVariableName();
return $tag instanceof Param && $allowedParam === $tag->getVariableName();
}));
return new DocBlock($docBlock->getSummary(), $docBlock->getDescription(), $tags, $docBlock->getContext(),
$docBlock->getLocation(), $docBlock->isTemplateStart(), $docBlock->isTemplateEnd());
return new DocBlock(
$docBlock->getSummary(),
$docBlock->getDescription(),
$tags,
$docBlock->getContext(),
$docBlock->getLocation(),
$docBlock->isTemplateStart(),
$docBlock->isTemplateEnd()
);
}
/**
@ -290,8 +305,8 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
{
// Use a ReflectionProperty instead of $class to get the parent class if applicable
try {
$reflectionProperty = new \ReflectionProperty($class, $property);
} catch (\ReflectionException $e) {
$reflectionProperty = new ReflectionProperty($class, $property);
} catch (ReflectionException $e) {
return null;
}
@ -305,7 +320,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
try {
return $this->docBlockFactory->create($reflectionProperty, $this->createFromReflector($reflector));
} catch (\InvalidArgumentException|\RuntimeException $e) {
} catch (InvalidArgumentException|RuntimeException $e) {
return null;
}
}
@ -315,25 +330,25 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
*/
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
{
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
$prefixes = $type === self::ACCESSOR ? $this->accessorPrefixes : $this->mutatorPrefixes;
$prefix = null;
foreach ($prefixes as $prefix) {
$methodName = $prefix.$ucFirstProperty;
$methodName = $prefix . $ucFirstProperty;
try {
$reflectionMethod = new \ReflectionMethod($class, $methodName);
$reflectionMethod = new ReflectionMethod($class, $methodName);
if ($reflectionMethod->isStatic()) {
continue;
}
if (
(self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters()) ||
(self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1)
($type === self::ACCESSOR && $reflectionMethod->getNumberOfRequiredParameters() === 0) ||
($type === self::MUTATOR && $reflectionMethod->getNumberOfParameters() >= 1)
) {
break;
}
} catch (\ReflectionException $e) {
} catch (ReflectionException $e) {
// Try the next prefix if the method doesn't exist
}
}
@ -352,7 +367,7 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
try {
return [$this->docBlockFactory->create($reflectionMethod, $this->createFromReflector($reflector)), $prefix];
} catch (\InvalidArgumentException|\RuntimeException $e) {
} catch (InvalidArgumentException|RuntimeException $e) {
return null;
}
}
@ -360,9 +375,9 @@ class CustomAnnotationExtractor implements PropertyDescriptionExtractorInterface
/**
* Prevents a lot of redundant calls to ContextFactory::createForNamespace().
*/
private function createFromReflector(\ReflectionClass $reflector): Context
private function createFromReflector(ReflectionClass $reflector): Context
{
$cacheKey = $reflector->getNamespaceName().':'.$reflector->getFileName();
$cacheKey = $reflector->getNamespaceName() . ':' . $reflector->getFileName();
if (isset($this->contexts[$cacheKey])) {
return $this->contexts[$cacheKey];

View file

@ -23,9 +23,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\EntityMapper;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* Registry with supported entities and their types.
*/
@ -70,10 +67,9 @@ class EntityRegistry
return '';
}
$matches = ArrayUtility::sortArraysByKey(
$matches,
'priority'
);
usort($matches, static function (array $configA, array $configB): int {
return $configA['priority'] <=> $configB['priority'];
});
return end($matches)['entityClassName'];
}

View file

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\EntityMapper;
use InvalidArgumentException;
use function str_starts_with;
use Symfony\Component\Serializer\Encoder\JsonDecode as SymfonyJsonDecode;
use TYPO3\CMS\Core\Utility\StringUtility;
/**
* Used to add further necessary normalization on decoding incoming JSON structure.
@ -50,14 +50,13 @@ class JsonDecode extends SymfonyJsonDecode
string $data,
string $format,
array $context = []
) {
): mixed {
$context[self::ASSOCIATIVE] = true;
$result = parent::decode($data, $format, $context);
$activeLanguage = $context[self::ACTIVE_LANGUAGE] ?? '';
if ($activeLanguage === '') {
throw new \InvalidArgumentException('Provide active language: ' . self::ACTIVE_LANGUAGE);
throw new InvalidArgumentException('Provide active language: ' . self::ACTIVE_LANGUAGE);
}
return $this->process(
@ -95,6 +94,7 @@ class JsonDecode extends SymfonyJsonDecode
* This decode will resolve the list to a single value based on current language settings from context.
*
* @param mixed $value
*
* @return mixed
*/
private function decodeLanguageSpecificValue(
@ -156,6 +156,7 @@ class JsonDecode extends SymfonyJsonDecode
* This decode will resolve single values wrapped in array with extra info.
*
* @param mixed $value
*
* @return mixed
*/
private function decodeSingleValues(
@ -190,6 +191,7 @@ class JsonDecode extends SymfonyJsonDecode
* Prepare data structure for PHP \DateTimeImmutable.
*
* @param mixed $value
*
* @return mixed
*/
private function decodeDateTime(
@ -214,6 +216,7 @@ class JsonDecode extends SymfonyJsonDecode
/**
* @param mixed $key
*
* @return mixed
*/
private function mapKey($key)
@ -222,13 +225,13 @@ class JsonDecode extends SymfonyJsonDecode
return $key;
}
if (StringUtility::beginsWith($key, '@')) {
if (str_starts_with($key, '@')) {
return mb_substr($key, 1);
}
if (StringUtility::beginsWith($key, 'schema:')) {
if (str_starts_with($key, 'schema:')) {
return mb_substr($key, 7);
}
if (StringUtility::beginsWith($key, 'thuecat:')) {
if (str_starts_with($key, 'thuecat:')) {
return mb_substr($key, 8);
}
@ -238,7 +241,7 @@ class JsonDecode extends SymfonyJsonDecode
private function doesRuleMatch(array $rule, string $type): bool
{
if ($rule['type'] === 'beginsWith') {
return StringUtility::beginsWith($type, $rule['comparisonValue']);
return str_starts_with($type, $rule['comparisonValue']);
}
return false;

View file

@ -23,7 +23,10 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\EntityMapper;
class MappingException extends \Exception
use Exception;
use Throwable;
class MappingException extends Exception
{
/**
* @var array
@ -38,7 +41,7 @@ class MappingException extends \Exception
public function __construct(
array $jsonLD,
string $targetClassName,
\Throwable $previous
Throwable $previous
) {
parent::__construct(
'Could not map incoming JSON-LD to target object: ' . $previous->getMessage(),

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import;
use InvalidArgumentException;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration as Typo3ImportConfiguration;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog;
@ -57,7 +58,7 @@ class Import
public function start(ImportConfiguration $configuration): void
{
if (!$configuration instanceof Typo3ImportConfiguration) {
throw new \InvalidArgumentException('Currently only can process ImportConfiguration of TYPO3.', 1629708772);
throw new InvalidArgumentException('Currently only can process ImportConfiguration of TYPO3.', 1629708772);
}
$this->currentConfiguration = $configuration;

View file

@ -23,12 +23,13 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import;
use TYPO3\CMS\Core\Log\LogManager;
use Exception;
use TYPO3\CMS\Core\Log\Logger;
use TYPO3\CMS\Core\Log\LogManager;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\EntityRegistry;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\JsonDecode;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\MappingException;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\Importer\Converter;
use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData;
use WerkraumMedia\ThueCat\Domain\Import\Importer\Languages;
@ -42,46 +43,6 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportLogRepository;
class Importer
{
/**
* @var UrlProviderRegistry
*/
private $urls;
/**
* @var Converter
*/
private $converter;
/**
* @var EntityRegistry
*/
private $entityRegistry;
/**
* @var EntityMapper
*/
private $entityMapper;
/**
* @var Languages
*/
private $languages;
/**
* @var FetchData
*/
private $fetchData;
/**
* @var SaveData
*/
private $saveData;
/**
* @var ImportLogRepository
*/
private $importLogRepository;
/**
* @var Logger
*/
@ -93,24 +54,16 @@ class Importer
private $import;
public function __construct(
UrlProviderRegistry $urls,
Converter $converter,
EntityRegistry $entityRegistry,
EntityMapper $entityMapper,
Languages $languages,
ImportLogRepository $importLogRepository,
FetchData $fetchData,
SaveData $saveData,
private readonly UrlProviderRegistry $urls,
private readonly Converter $converter,
private readonly EntityRegistry $entityRegistry,
private readonly EntityMapper $entityMapper,
private readonly Languages $languages,
private readonly ImportLogRepository $importLogRepository,
private readonly FetchData $fetchData,
private readonly SaveData $saveData,
LogManager $logManager
) {
$this->urls = $urls;
$this->converter = $converter;
$this->entityRegistry = $entityRegistry;
$this->entityMapper = $entityMapper;
$this->languages = $languages;
$this->importLogRepository = $importLogRepository;
$this->fetchData = $fetchData;
$this->saveData = $saveData;
$this->logger = $logManager->getLogger(__CLASS__);
$this->import = new Import();
}
@ -139,7 +92,7 @@ class Importer
{
$urlProvider = $this->urls->getProviderForConfiguration($this->import->getConfiguration());
if (!$urlProvider instanceof UrlProvider) {
throw new \Exception('No URL Provider available for given configuration.', 1629296635);
throw new Exception('No URL Provider available for given configuration.', 1629296635);
}
foreach ($urlProvider->getUrls() as $url) {
@ -218,7 +171,8 @@ class Importer
'url' => $url,
'language' => $language,
'targetEntity' => $targetEntity,
]);
]
);
continue;
}
$entities->add($convertedEntity);

View file

@ -32,21 +32,6 @@ use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData\InvalidResponseExcept
class FetchData
{
/**
* @var RequestFactoryInterface
*/
private $requestFactory;
/**
* @var ClientInterface
*/
private $httpClient;
/**
* @var CacheFrontendInterface
*/
private $cache;
/**
* @var string
*/
@ -58,13 +43,10 @@ class FetchData
private $urlPrefix = 'https://thuecat.org';
public function __construct(
RequestFactoryInterface $requestFactory,
ClientInterface $httpClient,
CacheFrontendInterface $cache
private readonly RequestFactoryInterface $requestFactory,
private readonly ClientInterface $httpClient,
private readonly CacheFrontendInterface $cache
) {
$this->requestFactory = $requestFactory;
$this->httpClient = $httpClient;
$this->cache = $cache;
}
public function updatedNodes(string $scopeId): array
@ -94,7 +76,7 @@ class FetchData
$this->handleInvalidResponse($response, $request);
$jsonLD = json_decode((string) $response->getBody(), true);
$jsonLD = json_decode((string)$response->getBody(), true);
if (is_array($jsonLD)) {
$this->cache->set($cacheIdentifier, $jsonLD);
return $jsonLD;

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData;
class InvalidResponseException extends \RuntimeException
use RuntimeException;
class InvalidResponseException extends RuntimeException
{
}

View file

@ -32,27 +32,15 @@ use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\SavingEntity;
class SaveData
{
/**
* @var DataHandler
*/
private $dataHandler;
/**
* @var ConnectionPool
*/
private $connectionPool;
/**
* @var mixed[]
*/
private $errorLog;
public function __construct(
DataHandler $dataHandler,
ConnectionPool $connectionPool
private readonly DataHandler $dataHandler,
private readonly ConnectionPool $connectionPool
) {
$this->dataHandler = $dataHandler;
$this->connectionPool = $connectionPool;
}
public function import(EntityCollection $entityCollection, ImportLog $log): void
@ -78,7 +66,7 @@ class SaveData
$identifier = $this->getIdentifier($entity);
if (is_numeric($identifier)) {
$entity->setExistingTypo3Uid((int) $identifier);
$entity->setExistingTypo3Uid((int)$identifier);
}
}
}
@ -155,7 +143,7 @@ class SaveData
$existingUid = $this->getExistingUid($entity);
if ($existingUid > 0) {
return (string) $existingUid;
return (string)$existingUid;
}
$identifier = 'NEW_' . sha1($entity->getRemoteId() . $entity->getTypo3SystemLanguageUid());
@ -176,7 +164,8 @@ class SaveData
$tableColumns = $this->connectionPool
->getConnectionForTable($entity->getTypo3DatabaseTableName())
->getSchemaManager()
->listTableColumns($entity->getTypo3DatabaseTableName());
->listTableColumns($entity->getTypo3DatabaseTableName())
;
$queryBuilder = $this->connectionPool->getQueryBuilderForTable($entity->getTypo3DatabaseTableName());
$queryBuilder->getRestrictions()->removeAll();
@ -193,9 +182,9 @@ class SaveData
));
}
$result = $queryBuilder->execute()->fetchColumn();
$result = $queryBuilder->executeQuery()->fetchOne();
if (is_numeric($result)) {
return (int) $result;
return (int)$result;
}
return 0;
@ -216,9 +205,9 @@ class SaveData
$queryBuilder->createNamedParameter(0)
));
$result = $queryBuilder->execute()->fetchColumn();
$result = $queryBuilder->executeQuery()->fetchOne();
if (is_numeric($result)) {
return (int) $result;
return (int)$result;
}
return 0;

View file

@ -61,8 +61,7 @@ class EntityCollection
{
return array_filter($this->entities, function (Entity $entity) {
return $entity->isTranslation()
&& $entity->exists() === false
;
&& $entity->exists() === false;
});
}

View file

@ -32,29 +32,11 @@ use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
class RequestFactory implements RequestFactoryInterface
{
/**
* @var ExtensionConfiguration
*/
private $extensionConfiguration;
/**
* @var RequestFactoryInterface
*/
private $requestFactory;
/**
* @var UriFactoryInterface
*/
private $uriFactory;
public function __construct(
ExtensionConfiguration $extensionConfiguration,
RequestFactoryInterface $requestFactory,
UriFactoryInterface $uriFactory
private readonly ExtensionConfiguration $extensionConfiguration,
private readonly RequestFactoryInterface $requestFactory,
private readonly UriFactoryInterface $uriFactory
) {
$this->extensionConfiguration = $extensionConfiguration;
$this->requestFactory = $requestFactory;
$this->uriFactory = $uriFactory;
}
/**
@ -63,7 +45,7 @@ class RequestFactory implements RequestFactoryInterface
public function createRequest(string $method, $uri): RequestInterface
{
if (!$uri instanceof UriInterface) {
$uri = $this->uriFactory->createUri((string) $uri);
$uri = $this->uriFactory->createUri((string)$uri);
}
$query = [];

View file

@ -23,9 +23,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\ForeignReference;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\EntityRegistry;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\JsonDecode;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\ForeignReference;
use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData;
use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData\InvalidResponseException;
@ -38,29 +38,11 @@ use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData\InvalidResponseExcept
*/
class ResolveForeignReference
{
/**
* @var FetchData
*/
private $fetchData;
/**
* @var EntityRegistry
*/
private $entityRegistry;
/**
* @var EntityMapper
*/
private $entityMapper;
public function __construct(
FetchData $fetchData,
EntityRegistry $entityRegistry,
EntityMapper $entityMapper
private readonly FetchData $fetchData,
private readonly EntityRegistry $entityRegistry,
private readonly EntityMapper $entityMapper
) {
$this->fetchData = $fetchData;
$this->entityRegistry = $entityRegistry;
$this->entityMapper = $entityMapper;
}
public function resolve(
@ -94,6 +76,7 @@ class ResolveForeignReference
/**
* @param ForeignReference[] $foreignReferences
*
* @return string[]
*/
public static function convertToRemoteIds(array $foreignReferences): array

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import;
use Exception;
use InvalidArgumentException;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\Importer\Converter;
use WerkraumMedia\ThueCat\Domain\Import\Model\Entity;
@ -32,15 +34,9 @@ use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration as Typo3Impor
class Typo3Converter implements Converter
{
/**
* @var Registry
*/
private $registry;
public function __construct(
Registry $registry
private readonly Registry $registry
) {
$this->registry = $registry;
}
public function convert(
@ -49,12 +45,12 @@ class Typo3Converter implements Converter
string $language
): ?Entity {
if (!$configuration instanceof Typo3ImportConfiguration) {
throw new \InvalidArgumentException('Only supports TYPO3 import configuration.', 1629710386);
throw new InvalidArgumentException('Only supports TYPO3 import configuration.', 1629710386);
}
$concreteConverter = $this->registry->getConverterBasedOnType($mapped);
if (!$concreteConverter instanceof Typo3ConcreteConverter) {
throw new \Exception(
throw new Exception(
'No TYPO3 Converter registered for given Entity "' . get_class($mapped) . '".',
1628244329
);

View file

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Typo3Converter;
use TYPO3\CMS\Core\Log\LogManager;
use Exception;
use TYPO3\CMS\Core\Log\Logger;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use WerkraumMedia\ThueCat\Domain\Import\Entity\AccessibilitySpecification;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Base;
@ -52,41 +53,6 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository;
class GeneralConverter implements Converter
{
/**
* @var ResolveForeignReference
*/
private $resolveForeignReference;
/**
* @var Importer
*/
private $importer;
/**
* @var LanguageHandling
*/
private $languageHandling;
/**
* @var OrganisationRepository
*/
private $organisationRepository;
/**
* @var TownRepository
*/
private $townRepository;
/**
* @var ParkingFacilityRepository
*/
private $parkingFacilityRepository;
/**
* @var NameExtractor
*/
private $nameExtractor;
/**
* @var Logger
*/
@ -110,22 +76,15 @@ class GeneralConverter implements Converter
];
public function __construct(
ResolveForeignReference $resolveForeignReference,
Importer $importer,
LanguageHandling $languageHandling,
OrganisationRepository $organisationRepository,
TownRepository $townRepository,
ParkingFacilityRepository $parkingFacilityRepository,
NameExtractor $nameExtractor,
private readonly ResolveForeignReference $resolveForeignReference,
private readonly Importer $importer,
private readonly LanguageHandling $languageHandling,
private readonly OrganisationRepository $organisationRepository,
private readonly TownRepository $townRepository,
private readonly ParkingFacilityRepository $parkingFacilityRepository,
private readonly NameExtractor $nameExtractor,
LogManager $logManager
) {
$this->resolveForeignReference = $resolveForeignReference;
$this->importer = $importer;
$this->languageHandling = $languageHandling;
$this->organisationRepository = $organisationRepository;
$this->townRepository = $townRepository;
$this->parkingFacilityRepository = $parkingFacilityRepository;
$this->nameExtractor = $nameExtractor;
$this->logger = $logManager->getLogger(__CLASS__);
}
@ -214,7 +173,7 @@ class GeneralConverter implements Converter
{
$tableName = $this->classToTableMapping[$className] ?? '';
if ($tableName == '') {
throw new \Exception('No table name configured for class ' . $className, 1629376990);
throw new Exception('No table name configured for class ' . $className, 1629376990);
}
return $tableName;
@ -298,7 +257,7 @@ class GeneralConverter implements Converter
)
);
$town = $this->townRepository->findOneByEntity($entity);
return $town ? (string) $town->getUid() : '';
return $town ? (string)$town->getUid() : '';
}
private function getParkingFacilitiesNearByUids(Base $entity): array

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\Typo3Converter;
use InvalidArgumentException;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Site\SiteFinder;
use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration;
@ -30,36 +31,30 @@ use WerkraumMedia\ThueCat\Domain\Import\Importer\Languages;
class LanguageHandling implements Languages
{
/**
* @var SiteFinder
*/
private $siteFinder;
public function __construct(
SiteFinder $siteFinder
private readonly SiteFinder $siteFinder
) {
$this->siteFinder = $siteFinder;
}
public function getAvailable(ImportConfiguration $configuration): array
{
if (method_exists($configuration, 'getStoragePid') === false) {
throw new \InvalidArgumentException('Unsupported configuration, need to retrieve storage pid.', 1629710300);
throw new InvalidArgumentException('Unsupported configuration, need to retrieve storage pid.', 1629710300);
}
return array_map(function (SiteLanguage $language) {
return $language->getTwoLetterIsoCode();
return $language->getLocale()->getLanguageCode();
}, $this->getLanguages($configuration->getStoragePid()));
}
public function getLanguageUidForString(int $pageUid, string $isoCode): int
{
foreach ($this->getLanguages($pageUid) as $language) {
if ($language->getTwoLetterIsoCode() === $isoCode) {
if ($language->getLocale()->getLanguageCode() === $isoCode) {
return $language->getLanguageId();
}
}
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Could not find language for combination of page "%d" and iso code "%s".',
$pageUid,

View file

@ -23,25 +23,20 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\UrlProvider;
use InvalidArgumentException;
use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration;
use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData;
class ContainsPlaceUrlProvider implements UrlProvider
{
/**
* @var FetchData
*/
private $fetchData;
/**
* @var string
*/
private $containsPlaceId = '';
public function __construct(
FetchData $fetchData
private readonly FetchData $fetchData
) {
$this->fetchData = $fetchData;
}
public function canProvideForConfiguration(
@ -54,7 +49,7 @@ class ContainsPlaceUrlProvider implements UrlProvider
ImportConfiguration $configuration
): UrlProvider {
if (method_exists($configuration, 'getContainsPlaceId') === false) {
throw new \InvalidArgumentException('Received incompatible import configuration.', 1629709276);
throw new InvalidArgumentException('Received incompatible import configuration.', 1629709276);
}
$instance = clone $this;
$instance->containsPlaceId = $configuration->getContainsPlaceId();

View file

@ -23,25 +23,20 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Import\UrlProvider;
use InvalidArgumentException;
use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration;
use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData;
class SyncScopeUrlProvider implements UrlProvider
{
/**
* @var FetchData
*/
private $fetchData;
/**
* @var string
*/
private $syncScopeId = '';
public function __construct(
FetchData $fetchData
private readonly FetchData $fetchData
) {
$this->fetchData = $fetchData;
}
public function canProvideForConfiguration(
@ -54,7 +49,7 @@ class SyncScopeUrlProvider implements UrlProvider
ImportConfiguration $configuration
): UrlProvider {
if (method_exists($configuration, 'getSyncScopeId') === false) {
throw new \InvalidArgumentException('Received incompatible import configuration.', 1629709276);
throw new InvalidArgumentException('Received incompatible import configuration.', 1629709276);
}
$instance = clone $this;
$instance->syncScopeId = $configuration->getSyncScopeId();

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Backend;
use DateTimeImmutable;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity as Typo3AbstractEntity;
class AbstractEntity extends Typo3AbstractEntity
@ -43,9 +44,9 @@ class AbstractEntity extends Typo3AbstractEntity
protected $description = '';
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
protected $tstamp = null;
protected $tstamp;
public function getRemoteId(): string
{
@ -62,7 +63,7 @@ class AbstractEntity extends Typo3AbstractEntity
return $this->description;
}
public function getLastImported(): ?\DateTimeImmutable
public function getLastImported(): ?DateTimeImmutable
{
return $this->tstamp;
}

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Backend;
use DateTimeImmutable;
use Exception;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
@ -49,9 +51,9 @@ class ImportConfiguration extends AbstractEntity implements ImportConfigurationI
protected $configuration = '';
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
protected $tstamp = null;
protected $tstamp;
/**
* @var ObjectStorage<ImportLog>
@ -61,7 +63,7 @@ class ImportConfiguration extends AbstractEntity implements ImportConfigurationI
/**
* @var string[]|null
*/
protected $urls = null;
protected $urls;
/**
* @var string[]
@ -88,15 +90,15 @@ class ImportConfiguration extends AbstractEntity implements ImportConfigurationI
return 'tx_thuecat_import_configuration';
}
public function getLastChanged(): ?\DateTimeImmutable
public function getLastChanged(): ?DateTimeImmutable
{
return $this->tstamp;
}
public function getLastImported(): ?\DateTimeImmutable
public function getLastImported(): ?DateTimeImmutable
{
$lastImport = null;
$positionOfLastLog = (string) (count($this->logs) - 1);
$positionOfLastLog = (count($this->logs) - 1);
if ($this->logs->offsetExists($positionOfLastLog)) {
$lastImport = $this->logs->offsetGet($positionOfLastLog);
}
@ -112,7 +114,7 @@ class ImportConfiguration extends AbstractEntity implements ImportConfigurationI
$storagePid = $this->getConfigurationValueFromFlexForm('storagePid');
if (is_numeric($storagePid) && $storagePid > 0) {
return intval($storagePid);
return (int)$storagePid;
}
return 0;
@ -151,7 +153,7 @@ class ImportConfiguration extends AbstractEntity implements ImportConfigurationI
{
$containsPlaceId = $this->getConfigurationValueFromFlexForm('containsPlaceId');
if (!is_string($containsPlaceId)) {
throw new \Exception('Could not fetch containsPlaceId.', 1671027015);
throw new Exception('Could not fetch containsPlaceId.', 1671027015);
}
return $containsPlaceId;
}

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Backend;
use DateTimeImmutable;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity as Typo3AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\SavingEntity;
@ -37,10 +38,10 @@ class ImportLog extends Typo3AbstractEntity
/**
* @var ImportConfiguration|null
*/
protected $configuration = null;
protected $configuration;
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
protected $crdate;
@ -78,7 +79,7 @@ class ImportLog extends Typo3AbstractEntity
return $this->logEntries;
}
public function getCreated(): ?\DateTimeImmutable
public function getCreated(): ?DateTimeImmutable
{
return $this->crdate;
}

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry;
use Exception;
use WerkraumMedia\ThueCat\Domain\Import\EntityMapper\MappingException;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry;
@ -54,7 +55,7 @@ class MappingError extends ImportLogEntry
{
$errors = json_decode($this->errors, true);
if (is_array($errors) === false) {
throw new \Exception('Could not parse errors.', 1671097690);
throw new Exception('Could not parse errors.', 1671097690);
}
return $errors;
}

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry;
use Exception;
use WerkraumMedia\ThueCat\Domain\Import\Model\Entity;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry;
@ -100,7 +101,7 @@ class SavingEntity extends ImportLogEntry
if ($this->errorsAsArray === [] && $this->errors !== '') {
$errorsAsArray = json_decode($this->errors, true);
if (is_array($errorsAsArray) === false) {
throw new \Exception('Could not parse errors.', 1671097690);
throw new Exception('Could not parse errors.', 1671097690);
}
$this->errorsAsArray = array_unique($errorsAsArray);
}
@ -121,7 +122,7 @@ class SavingEntity extends ImportLogEntry
public function getInsertion(): array
{
return [
'insertion' => (int) $this->wasInsertion(),
'insertion' => (int)$this->wasInsertion(),
'record_uid' => $this->getRecordUid(),
'table_name' => $this->getRecordDatabaseTableName(),
];

View file

@ -40,7 +40,7 @@ abstract class Base extends AbstractEntity
/**
* @var Media|null
*/
protected $media = null;
protected $media;
public function getTitle(): string
{

View file

@ -94,6 +94,7 @@ class Media implements TypeInterface
/**
* @internal Only used to set the values while mapping objects.
*
* @see: AfterObjectThawedHandler
*/
public function setEditorialImages(array $images): void

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
use DateTimeImmutable;
class MergedOpeningHour
{
/**
@ -31,19 +33,19 @@ class MergedOpeningHour
private $weekDays = [];
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
private $from;
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
private $through;
public function __construct(
array $weekDays,
?\DateTimeImmutable $from,
?\DateTimeImmutable $through
?DateTimeImmutable $from,
?DateTimeImmutable $through
) {
$this->weekDays = array_values($weekDays);
$this->from = $from;
@ -72,12 +74,12 @@ class MergedOpeningHour
]);
}
public function getFrom(): ?\DateTimeImmutable
public function getFrom(): ?DateTimeImmutable
{
return $this->from;
}
public function getThrough(): ?\DateTimeImmutable
public function getThrough(): ?DateTimeImmutable
{
return $this->through;
}

View file

@ -23,7 +23,10 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
class MergedOpeningHours implements \Iterator, \Countable
use Countable;
use Iterator;
class MergedOpeningHours implements Iterator, Countable
{
/**
* @var MergedOpeningHour[]

View file

@ -95,7 +95,7 @@ class Offer
public function getType(): string
{
$offerTypes = array_filter($this->types, function (string $type) {
return strpos($type, 'Offer') !== false;
return str_contains($type, 'Offer');
});
// Ensure clean index
$offerTypes = array_values($offerTypes);

View file

@ -23,12 +23,14 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
use Countable;
use Iterator;
use TYPO3\CMS\Core\Type\TypeInterface;
/**
* @implements \Iterator<int, Offer>
*/
class Offers implements TypeInterface, \Iterator, \Countable
class Offers implements TypeInterface, Iterator, Countable
{
/**
* @var string

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
use DateTimeImmutable;
use DateTimeZone;
use WerkraumMedia\ThueCat\Domain\TimingFormat;
class OpeningHour
@ -43,12 +45,12 @@ class OpeningHour
private $daysOfWeek;
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
private $from;
/**
* @var \DateTimeImmutable|null
* @var DateTimeImmutable|null
*/
private $through;
@ -56,8 +58,8 @@ class OpeningHour
string $opens,
string $closes,
array $daysOfWeek,
?\DateTimeImmutable $from,
?\DateTimeImmutable $through
?DateTimeImmutable $from,
?DateTimeImmutable $through
) {
$this->opens = $opens;
$this->closes = $closes;
@ -73,13 +75,13 @@ class OpeningHour
{
$from = null;
if (isset($rawData['from'])) {
$timeZone = new \DateTimeZone($rawData['from']['timezone'] ?? 'Europe/Berlin');
$from = new \DateTimeImmutable($rawData['from']['date'], $timeZone);
$timeZone = new DateTimeZone($rawData['from']['timezone'] ?? 'Europe/Berlin');
$from = new DateTimeImmutable($rawData['from']['date'], $timeZone);
}
$through = null;
if (isset($rawData['through'])) {
$timeZone = new \DateTimeZone($rawData['through']['timezone'] ?? 'Europe/Berlin');
$through = new \DateTimeImmutable($rawData['through']['date'], $timeZone);
$timeZone = new DateTimeZone($rawData['through']['timezone'] ?? 'Europe/Berlin');
$through = new DateTimeImmutable($rawData['through']['date'], $timeZone);
}
return new self(
@ -120,12 +122,12 @@ class OpeningHour
]);
}
public function getFrom(): ?\DateTimeImmutable
public function getFrom(): ?DateTimeImmutable
{
return $this->from;
}
public function getThrough(): ?\DateTimeImmutable
public function getThrough(): ?DateTimeImmutable
{
return $this->through;
}
@ -135,10 +137,9 @@ class OpeningHour
$from = $this->getFrom();
$through = $this->getThrough();
return $from instanceof \DateTimeImmutable
&& $through instanceof \DateTimeImmutable
&& $from->format('Ymd') === $through->format('Ymd')
;
return $from instanceof DateTimeImmutable
&& $through instanceof DateTimeImmutable
&& $from->format('Ymd') === $through->format('Ymd');
}
private function sortedDaysOfWeek(array $sorting): array

View file

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Model\Frontend;
use Countable;
use DateTimeImmutable;
use Iterator;
use TYPO3\CMS\Core\Type\TypeInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use WerkraumMedia\ThueCat\Service\DateBasedFilter;
@ -30,7 +33,7 @@ use WerkraumMedia\ThueCat\Service\DateBasedFilter;
/**
* @implements \Iterator<int, OpeningHour>
*/
class OpeningHours implements TypeInterface, \Iterator, \Countable
class OpeningHours implements TypeInterface, Iterator, Countable
{
/**
* @var string
@ -63,10 +66,11 @@ class OpeningHours implements TypeInterface, \Iterator, \Countable
$array = GeneralUtility::makeInstance(DateBasedFilter::class)
->filterOutPreviousDates(
$array,
function (OpeningHour $hour): ?\DateTimeImmutable {
function (OpeningHour $hour): ?DateTimeImmutable {
return $hour->getThrough();
}
);
)
;
usort($array, function (OpeningHour $hourA, OpeningHour $hourB) {
return $hourA->getFrom() <=> $hourB->getFrom();

View file

@ -31,7 +31,7 @@ abstract class Place extends Base
/**
* @var Address|null
*/
protected $address = null;
protected $address;
/**
* @var string
@ -41,12 +41,12 @@ abstract class Place extends Base
/**
* @var OpeningHours|null
*/
protected $openingHours = null;
protected $openingHours;
/**
* @var OpeningHours|null
*/
protected $specialOpeningHours = null;
protected $specialOpeningHours;
/**
* @var ObjectStorage<ParkingFacility>
@ -81,7 +81,7 @@ abstract class Place extends Base
/**
* @var AccessiblitySpecification|null
*/
protected $accessibilitySpecification = null;
protected $accessibilitySpecification;
public function initializeObject(): void
{

View file

@ -35,12 +35,12 @@ class TouristAttraction extends Place
/**
* @var Offers|null
*/
protected $offers = null;
protected $offers;
/**
* @var Town|null
*/
protected $town = null;
protected $town;
/**
* @var string

View file

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Repository\Backend;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\Repository;
use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration;
@ -31,10 +30,9 @@ use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration;
class ImportConfigurationRepository extends Repository
{
public function __construct(
ObjectManagerInterface $objectManager,
Typo3QuerySettings $querySettings
) {
parent::__construct($objectManager);
parent::__construct();
$querySettings->setRespectStoragePage(false);

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Repository\Backend;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
@ -32,19 +31,11 @@ use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog;
class ImportLogRepository extends Repository
{
/**
* @var DataHandler
*/
private $dataHandler;
public function __construct(
ObjectManagerInterface $objectManager,
DataHandler $dataHandler,
private readonly DataHandler $dataHandler,
Typo3QuerySettings $querySettings
) {
parent::__construct($objectManager);
$this->dataHandler = $dataHandler;
parent::__construct();
$querySettings->setRespectStoragePage(false);
$this->setDefaultQuerySettings($querySettings);

View file

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Repository\Backend;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\Repository;
use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation;
@ -34,10 +33,9 @@ use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation;
class OrganisationRepository extends Repository
{
public function __construct(
ObjectManagerInterface $objectManager,
Typo3QuerySettings $querySettings
) {
parent::__construct($objectManager);
parent::__construct();
$querySettings->setRespectStoragePage(false);
$this->setDefaultQuerySettings($querySettings);

View file

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Repository\Backend;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
@ -32,10 +31,9 @@ use WerkraumMedia\ThueCat\Domain\Import\Entity\Properties\ForeignReference;
class ParkingFacilityRepository extends Repository
{
public function __construct(
ObjectManagerInterface $objectManager,
Typo3QuerySettings $querySettings
) {
parent::__construct($objectManager);
parent::__construct();
$querySettings->setRespectStoragePage(false);

View file

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Domain\Repository\Backend;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\Repository;
use WerkraumMedia\ThueCat\Domain\Import\ResolveForeignReference;
@ -32,10 +31,9 @@ use WerkraumMedia\ThueCat\Domain\Model\Backend\Town;
class TownRepository extends Repository
{
public function __construct(
ObjectManagerInterface $objectManager,
Typo3QuerySettings $querySettings
) {
parent::__construct($objectManager);
parent::__construct();
$querySettings->setRespectStoragePage(false);

View file

@ -24,13 +24,9 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat;
use TYPO3\CMS\Core\Cache\Backend\TransientMemoryBackend;
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
use TYPO3\CMS\Core\Imaging\IconRegistry;
use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
use WerkraumMedia\ThueCat\Controller\Backend\ConfigurationController;
use WerkraumMedia\ThueCat\Controller\Backend\ImportController;
class Extension
{
@ -47,57 +43,11 @@ class Extension
return 'LLL:EXT:' . self::EXTENSION_KEY . '/Resources/Private/Language/';
}
public static function registerBackendModules(): void
{
ExtensionUtility::registerModule(
self::EXTENSION_NAME,
'thuecat',
'',
'',
[],
[
'access' => 'user,group',
'icon' => self::getIconPath() . 'ModuleGroup.svg',
'labels' => self::getLanguagePath() . 'locallang_mod.xlf',
]
);
ExtensionUtility::registerModule(
self::EXTENSION_NAME,
'thuecat',
'configurations',
'',
[
ConfigurationController::class => 'index',
ImportController::class => 'import',
],
[
'access' => 'user,group',
'icon' => self::getIconPath() . 'ModuleConfigurations.svg',
'labels' => self::getLanguagePath() . 'locallang_mod_configurations.xlf',
]
);
ExtensionUtility::registerModule(
self::EXTENSION_NAME,
'thuecat',
'imports',
'',
[
ImportController::class => 'index,import',
],
[
'access' => 'user,group',
'icon' => self::getIconPath() . 'ModuleImports.svg',
'labels' => self::getLanguagePath() . 'locallang_mod_imports.xlf',
]
);
}
public static function registerConfig(): void
{
self::addCaching();
self::addContentElements();
self::addPageTypes();
self::addIcons();
}
public static function getIconPath(): string
@ -129,28 +79,20 @@ class Extension
private static function addPageTypes(): void
{
$registry = GeneralUtility::makeInstance(PageDoktypeRegistry::class);
$registry->add(
self::PAGE_DOKTYPE_TOURIST_ATTRACTION,
[
'type' => 'web',
'allowedTables' => '*',
]
);
ExtensionManagementUtility::addUserTSConfig(
"@import 'EXT:" . self::EXTENSION_KEY . "/Configuration/TSconfig/User/All.tsconfig'"
);
}
private static function addIcons(): void
{
$iconFiles = GeneralUtility::getFilesInDir(GeneralUtility::getFileAbsFileName(self::getIconPath()));
if (is_array($iconFiles) === false) {
return;
}
$iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
foreach ($iconFiles as $iconFile) {
$iconRegistry->registerIcon(
str_replace('.svg', '', $iconFile),
SvgIconProvider::class,
['source' => self::getIconPath() . $iconFile]
);
}
}
private static function addCaching(): void
{
$cacheIdentifier = 'thuecat_fetchdata';

View file

@ -33,27 +33,15 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
class ResolveEntities implements DataProcessorInterface
{
/**
* @var ConnectionPool
*/
private $connectionPool;
/**
* @var DataMapper
*/
private $dataMapper;
/**
* @var TypoScriptFrontendController
*/
private $tsfe;
public function __construct(
ConnectionPool $connectionPool,
DataMapper $dataMapper
private readonly ConnectionPool $connectionPool,
private readonly DataMapper $dataMapper
) {
$this->connectionPool = $connectionPool;
$this->dataMapper = $dataMapper;
$this->tsfe = $GLOBALS['TSFE'];
}
@ -67,9 +55,9 @@ class ResolveEntities implements DataProcessorInterface
return $processedData;
}
$as = (string) $cObj->stdWrapValue('as', $processorConfiguration, 'entities');
$tableName = (string) $cObj->stdWrapValue('table', $processorConfiguration, '');
$uids = (string) $cObj->stdWrapValue('uids', $processorConfiguration, '');
$as = (string)$cObj->stdWrapValue('as', $processorConfiguration, 'entities');
$tableName = (string)$cObj->stdWrapValue('table', $processorConfiguration, '');
$uids = (string)$cObj->stdWrapValue('uids', $processorConfiguration, '');
$uids = GeneralUtility::intExplode(',', $uids);
if ($uids === [] || $tableName === '') {
@ -93,14 +81,14 @@ class ResolveEntities implements DataProcessorInterface
));
$rows = [];
foreach ($queryBuilder->execute() as $row) {
foreach ($queryBuilder->executeQuery()->iterateAssociative() as $row) {
$row = $this->tsfe->sys_page->getLanguageOverlay($tableName, $row);
if (is_array($row)) {
$rows[] = $row;
}
}
usort($rows, function (array $rowA, array $rowB) use($uids) {
usort($rows, function (array $rowA, array $rowB) use ($uids) {
return array_search($rowA['uid'], $uids) <=> array_search($rowB['uid'], $uids);
});

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Service\DateBasedFilter;
use DateTimeImmutable;
use TYPO3\CMS\Core\Context\Context;
use WerkraumMedia\ThueCat\Service\DateBasedFilter;
@ -48,9 +49,9 @@ class FilterBasedOnTypo3Context implements DateBasedFilter
array $listToFilter,
callable $provideDate
): array {
$referenceDate = $this->context->getPropertyFromAspect('date', 'full', new \DateTimeImmutable());
$referenceDate = $this->context->getPropertyFromAspect('date', 'full', new DateTimeImmutable());
return array_filter($listToFilter, function($elementToFilter) use ($referenceDate, $provideDate) {
return array_filter($listToFilter, function ($elementToFilter) use ($referenceDate, $provideDate) {
$objectDate = $provideDate($elementToFilter);
return $objectDate === null || $objectDate >= $referenceDate;
});

View file

@ -25,19 +25,16 @@ namespace WerkraumMedia\ThueCat\Updates;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Attribute\UpgradeWizard;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
#[UpgradeWizard('thuecat_backendmoduleuserpermission_v12')]
class BackendModuleUserPermission implements UpgradeWizardInterface
{
/**
* @var ConnectionPool
*/
private $connectionPool;
public function __construct()
{
$this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
public function __construct(
private readonly ConnectionPool $connectionPool
) {
}
public function getIdentifier(): string
@ -62,8 +59,9 @@ class BackendModuleUserPermission implements UpgradeWizardInterface
$qb->count('*');
$qb->from('be_users');
$qb->where($qb->expr()->like('userMods', $qb->createNamedParameter('%site_ThuecatThuecat%')));
$qb->orWhere($qb->expr()->like('userMods', $qb->createNamedParameter('%ThuecatThuecat%')));
return $qb->execute()->fetchOne() > 0;
return $qb->executeQuery()->fetchOne() > 0;
}
public function executeUpdate(): bool
@ -73,14 +71,15 @@ class BackendModuleUserPermission implements UpgradeWizardInterface
$qb->select('uid', 'userMods');
$qb->from('be_users');
$qb->where($qb->expr()->like('userMods', $qb->createNamedParameter('%site_ThuecatThuecat%')));
$result = $qb->execute();
$qb->orWhere($qb->expr()->like('userMods', $qb->createNamedParameter('%ThuecatThuecat%')));
$result = $qb->executeQuery()->iterateAssociative();
foreach ($result as $backendUser) {
$qb = $this->connectionPool->getQueryBuilderForTable('be_users');
$qb->update('be_users');
$qb->set('userMods', $this->updateMods($backendUser['userMods']));
$qb->where($qb->expr()->eq('uid', $qb->createNamedParameter($backendUser['uid'])));
$qb->execute();
$qb->executeStatement();
}
return true;
@ -90,11 +89,15 @@ class BackendModuleUserPermission implements UpgradeWizardInterface
{
$mods = GeneralUtility::trimExplode(',', $mods, true);
unset($mods[array_search('site_ThuecatThuecat', $mods)]);
unset(
$mods[array_search('site_ThuecatThuecat', $mods)],
$mods[array_search('ThuecatThuecat', $mods)],
$mods[array_search('ThuecatThuecat_ThuecatConfigurations', $mods)],
$mods[array_search('ThuecatThuecat_ThuecatImports', $mods)],
);
$mods[] = 'ThuecatThuecat';
$mods[] = 'ThuecatThuecat_ThuecatConfigurations';
$mods[] = 'ThuecatThuecat_ThuecatImports';
$mods[] = 'thuecat_configurations';
$mods[] = 'thuecat_imports';
return implode(',', $mods);
}
@ -105,9 +108,4 @@ class BackendModuleUserPermission implements UpgradeWizardInterface
DatabaseUpdatedPrerequisite::class,
];
}
public static function register(): void
{
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][self::class] = self::class;
}
}

View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Controller\Backend\ConfigurationController;
use WerkraumMedia\ThueCat\Controller\Backend\ImportController;
use WerkraumMedia\ThueCat\Extension;
return [
'thuecat_thuecat' => [
'icon' => Extension::getIconPath() . 'ModuleGroup.svg',
'position' => [
'after' => 'web',
'before' => 'file',
],
'labels' => 'LLL:EXT:thuecat/Resources/Private/Language/locallang_mod.xlf',
'extensionName' => 'Thuecat',
],
'thuecat_configurations' => [
'parent' => 'thuecat_thuecat',
'access' => 'user',
'icon' => Extension::getIconPath() . 'ModuleConfigurations.svg',
'labels' => 'LLL:EXT:thuecat/Resources/Private/Language/locallang_mod_configurations.xlf',
'extensionName' => 'Thuecat',
'controllerActions' => [
ConfigurationController::class => [
'index',
],
ImportController::class => [
'import',
],
],
],
'thuecat_imports' => [
'parent' => 'thuecat_thuecat',
'access' => 'user',
'icon' => Extension::getIconPath() . 'ModuleImports.svg',
'labels' => 'LLL:EXT:thuecat/Resources/Private/Language/locallang_mod_imports.xlf',
'extensionName' => 'Thuecat',
'controllerActions' => [
ImportController::class => [
'index',
'import',
],
],
],
];

View file

@ -1,41 +1,53 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\MappingError;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\SavingEntity;
use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation;
use WerkraumMedia\ThueCat\Domain\Model\Backend\ParkingFacility;
use WerkraumMedia\ThueCat\Domain\Model\Backend\TouristInformation;
use WerkraumMedia\ThueCat\Domain\Model\Backend\Town;
use WerkraumMedia\ThueCat\Domain\Model\Frontend\TouristAttraction;
return [
\WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation::class => [
Organisation::class => [
'tableName' => 'tx_thuecat_organisation',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\Town::class => [
Town::class => [
'tableName' => 'tx_thuecat_town',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\TouristInformation::class => [
TouristInformation::class => [
'tableName' => 'tx_thuecat_tourist_information',
],
WerkraumMedia\ThueCat\Domain\Model\Backend\ParkingFacility::class => [
ParkingFacility::class => [
'tableName' => 'tx_thuecat_parking_facility',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration::class => [
ImportConfiguration::class => [
'tableName' => 'tx_thuecat_import_configuration',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog::class => [
ImportLog::class => [
'tableName' => 'tx_thuecat_import_log',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry::class => [
ImportLogEntry::class => [
'tableName' => 'tx_thuecat_import_log_entry',
'subclasses' => [
'savingEntity' => \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\SavingEntity::class,
'mappingError' => \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\MappingError::class,
'savingEntity' => SavingEntity::class,
'mappingError' => MappingError::class,
],
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\SavingEntity::class => [
SavingEntity::class => [
'tableName' => 'tx_thuecat_import_log_entry',
'recordType' => 'savingEntity',
],
\WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry\MappingError::class => [
MappingError::class => [
'tableName' => 'tx_thuecat_import_log_entry',
'recordType' => 'mappingError',
],
\WerkraumMedia\ThueCat\Domain\Model\Frontend\TouristAttraction::class => [
TouristAttraction::class => [
'tableName' => 'tx_thuecat_tourist_attraction',
],
\WerkraumMedia\ThueCat\Domain\Model\Frontend\Town::class => [

30
Configuration/Icons.php Normal file
View file

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use WerkraumMedia\ThueCat\Extension;
return (static function (): array {
$iconFiles = GeneralUtility::getFilesInDir(
GeneralUtility::getFileAbsFileName(
Extension::getIconPath()
)
);
if (is_array($iconFiles) === false) {
return [];
}
$icons = [];
foreach ($iconFiles as $iconFile) {
$identifier = str_replace('.svg', '', $iconFile);
$icons[$identifier] = [
'provider' => SvgIconProvider::class,
'source' => Extension::getIconPath() . $iconFile,
];
}
return $icons;
})();

View file

@ -6,17 +6,20 @@ namespace WerkraumMedia\ThueCat;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use WerkraumMedia\ThueCat\DependencyInjection\ConverterPass;
use WerkraumMedia\ThueCat\DependencyInjection\EntityPass;
use WerkraumMedia\ThueCat\DependencyInjection\UrlProvidersPass;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\Typo3Converter\Converter;
use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\UrlProvider;
return function (ContainerConfigurator $container, ContainerBuilder $containerBuilder) {
$containerBuilder->registerForAutoconfiguration(UrlProvider::class)->addTag(DependencyInjection\UrlProvidersPass::TAG);
$containerBuilder->addCompilerPass(new DependencyInjection\UrlProvidersPass());
$containerBuilder->registerForAutoconfiguration(UrlProvider::class)->addTag(UrlProvidersPass::TAG);
$containerBuilder->addCompilerPass(new UrlProvidersPass());
$containerBuilder->registerForAutoconfiguration(Converter::class)->addTag(DependencyInjection\ConverterPass::TAG);
$containerBuilder->addCompilerPass(new DependencyInjection\ConverterPass());
$containerBuilder->registerForAutoconfiguration(Converter::class)->addTag(ConverterPass::TAG);
$containerBuilder->addCompilerPass(new ConverterPass());
$containerBuilder->registerForAutoconfiguration(MapsToType::class)->addTag(DependencyInjection\EntityPass::TAG);
$containerBuilder->addCompilerPass(new DependencyInjection\EntityPass());
$containerBuilder->registerForAutoconfiguration(MapsToType::class)->addTag(EntityPass::TAG);
$containerBuilder->addCompilerPass(new EntityPass());
};

View file

@ -1,12 +1,18 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
(static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath()
$languagePath = Extension::getLanguagePath()
. 'locallang_tca.xlf:' . $tableName;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
'ctrl' => [
'typeicon_classes' => [
'contains-thuecat' => 'pages_module_thuecat',
@ -32,23 +38,23 @@ defined('TYPO3') or die();
],
]);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItemGroup(
ExtensionManagementUtility::addTcaSelectItemGroup(
$tableName,
'doktype',
\WerkraumMedia\ThueCat\Extension::TCA_SELECT_GROUP_IDENTIFIER,
Extension::TCA_SELECT_GROUP_IDENTIFIER,
$languagePath . '.group'
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
ExtensionManagementUtility::addTcaSelectItem(
$tableName,
'module',
[
0 => $languagePath . '.module.thuecat',
1 => 'thuecat',
2 => 'pages_module_thuecat',
'label' => $languagePath . '.module.thuecat',
'value' => 'thuecat',
'icon' => 'pages_module_thuecat',
]
);
})(
\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY,
Extension::EXTENSION_KEY,
'pages'
);

View file

@ -1,12 +1,18 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
(static function (string $extensionKey, string $tableName, int $doktype, string $pageIdentifier) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath()
$languagePath = Extension::getLanguagePath()
. 'locallang_tca.xlf:' . $tableName . '.' . $pageIdentifier;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
'ctrl' => [
'typeicon_classes' => [
$doktype => $tableName . '_' . $pageIdentifier,
@ -54,19 +60,19 @@ defined('TYPO3') or die();
],
]);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
ExtensionManagementUtility::addTcaSelectItem(
$tableName,
'doktype',
[
$languagePath,
$doktype,
\WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '_' . $pageIdentifier . '.svg',
\WerkraumMedia\ThueCat\Extension::TCA_SELECT_GROUP_IDENTIFIER,
'label' => $languagePath,
'value' => $doktype,
'icon' => Extension::getIconPath() . $tableName . '_' . $pageIdentifier . '.svg',
'group' => Extension::TCA_SELECT_GROUP_IDENTIFIER,
]
);
})(
\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY,
Extension::EXTENSION_KEY,
'pages',
\WerkraumMedia\ThueCat\Extension::PAGE_DOKTYPE_TOURIST_ATTRACTION,
Extension::PAGE_DOKTYPE_TOURIST_ATTRACTION,
'tourist_attraction'
);

View file

@ -1,19 +1,24 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
(static function (string $extensionKey, string $tableName) {
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
ExtensionManagementUtility::addStaticFile(
$extensionKey,
'Configuration/TypoScript/ContentElements',
'ThüCAT - Content Elements'
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
ExtensionManagementUtility::addStaticFile(
$extensionKey,
'Configuration/TypoScript/PageTypes',
'ThüCAT - Page Types'
);
})(
\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY,
Extension::EXTENSION_KEY,
'sys_template'
);

View file

@ -1,18 +1,23 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
(static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath()
$languagePath = Extension::getLanguagePath()
. 'locallang_tca.xlf:' . $tableName;
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItemGroup(
ExtensionManagementUtility::addTcaSelectItemGroup(
$tableName,
'CType',
\WerkraumMedia\ThueCat\Extension::TCA_SELECT_GROUP_IDENTIFIER,
Extension::TCA_SELECT_GROUP_IDENTIFIER,
$languagePath . '.group'
);
})(
\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY,
Extension::EXTENSION_KEY,
'tt_content'
);

View file

@ -1,12 +1,18 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
(static function (string $extensionKey, string $tableName, string $cType) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath()
$languagePath = Extension::getLanguagePath()
. 'locallang_tca.xlf:' . $tableName . '.' . $cType;
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA'][$tableName], [
'ctrl' => [
'typeicon_classes' => [
$cType => 'tt_content_' . $cType,
@ -48,18 +54,18 @@ defined('TYPO3') or die();
],
]);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
ExtensionManagementUtility::addTcaSelectItem(
$tableName,
'CType',
[
$languagePath,
$cType,
\WerkraumMedia\ThueCat\Extension::getIconPath() . 'tt_content_' . $cType . '.svg',
\WerkraumMedia\ThueCat\Extension::TCA_SELECT_GROUP_IDENTIFIER,
'label' => $languagePath,
'value' => $cType,
'icon' => Extension::getIconPath() . 'tt_content_' . $cType . '.svg',
'group' => Extension::TCA_SELECT_GROUP_IDENTIFIER,
]
);
})(
\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY,
Extension::EXTENSION_KEY,
'tt_content',
'thuecat_tourist_attraction'
);

View file

@ -1,20 +1,23 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . \WerkraumMedia\ThueCat\Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'type' => 'type',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -28,7 +31,8 @@ return (static function (string $extensionKey, string $tableName) {
'config' => [
'type' => 'input',
'max' => 255,
'eval' => 'required,trim,unique',
'eval' => 'trim,unique',
'required' => true,
],
],
'type' => [
@ -38,16 +42,16 @@ return (static function (string $extensionKey, string $tableName) {
'renderType' => 'selectSingle',
'items' => [
[
$languagePath . '.type.static',
'static',
'label' => $languagePath . '.type.static',
'value' => 'static',
],
[
$languagePath . '.type.syncScope',
'syncScope',
'label' => $languagePath . '.type.syncScope',
'value' => 'syncScope',
],
[
$languagePath . '.type.containsPlace',
'containsPlace',
'label' => $languagePath . '.type.containsPlace',
'value' => 'containsPlace',
],
],
],
@ -67,9 +71,8 @@ return (static function (string $extensionKey, string $tableName) {
],
'tstamp' => [
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
'type' => 'datetime',
'format' => 'datetime',
'readOnly' => true,
],
],
@ -89,4 +92,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_configuration');
})(Extension::EXTENSION_KEY, 'tx_thuecat_import_configuration');

View file

@ -1,21 +1,24 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . \WerkraumMedia\ThueCat\Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
return [
'ctrl' => [
'label' => 'crdate',
'label_alt' => 'configuration',
'label_alt_force' => true,
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'crdate desc',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -44,9 +47,8 @@ return (static function (string $extensionKey, string $tableName) {
'crdate' => [
'label' => $languagePath . '.crdate',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
'type' => 'datetime',
'format' => 'datetime',
'readOnly' => true,
],
],
@ -57,4 +59,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_log');
})(Extension::EXTENSION_KEY, 'tx_thuecat_import_log');

View file

@ -1,22 +1,25 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . \WerkraumMedia\ThueCat\Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$flexFormConfigurationPath = 'FILE:EXT:' . Extension::EXTENSION_KEY . '/Configuration/FlexForm/';
return [
'ctrl' => [
'label' => 'type',
'label_alt' => 'remote_id, table_name, record_uid',
'label_alt_force' => true,
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'type' => 'type',
'default_sortby' => 'crdate',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -32,12 +35,12 @@ return (static function (string $extensionKey, string $tableName) {
'renderType' => 'selectSingle',
'items' => [
[
$languagePath . '.type.savingEntity',
'savingEntity',
'label' => $languagePath . '.type.savingEntity',
'value' => 'savingEntity',
],
[
$languagePath . '.type.mappingError',
'mappingError',
'label' => $languagePath . '.type.mappingError',
'value' => 'mappingError',
],
],
],
@ -56,7 +59,7 @@ return (static function (string $extensionKey, string $tableName) {
'renderType' => 'checkboxLabeledToggle',
'items' => [
[
0 => '',
'label' => '',
1 => '',
'labelChecked' => $languagePath . '.insertion.yes',
'labelUnchecked' => $languagePath . '.insertion.no',
@ -98,9 +101,8 @@ return (static function (string $extensionKey, string $tableName) {
'crdate' => [
'label' => $languagePath . '.crdate',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
'type' => 'datetime',
'format' => 'datetime',
'readOnly' => true,
],
],
@ -120,4 +122,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_log_entry');
})(Extension::EXTENSION_KEY, 'tx_thuecat_import_log_entry');

View file

@ -1,18 +1,21 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -73,9 +76,8 @@ return (static function (string $extensionKey, string $tableName) {
'tstamp' => [
'label' => $languagePath . '.tstamp',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
'type' => 'datetime',
'format' => 'datetime',
'readOnly' => true,
],
],
@ -88,4 +90,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_organisation');
})(Extension::EXTENSION_KEY, 'tx_thuecat_organisation');

View file

@ -1,18 +1,21 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -27,19 +30,7 @@ return (static function (string $extensionKey, string $tableName) {
'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,
],
'config' => ['type' => 'language'],
],
'l18n_parent' => [
'displayCond' => 'FIELD:sys_language_uid:>:0',
@ -47,7 +38,7 @@ return (static function (string $extensionKey, string $tableName) {
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'items' => [['', 0]],
'items' => [['label' => '', 'value' => 0]],
'foreign_table' => $tableName,
'foreign_table_where' => 'AND ' . $tableName . '.pid=###CURRENT_PID### AND ' . $tableName . '.sys_language_uid IN (-1,0)',
'default' => 0,
@ -66,12 +57,12 @@ return (static function (string $extensionKey, string $tableName) {
'renderType' => 'checkboxToggle',
'items' => [
[
0 => '',
'label' => '',
1 => '',
'invertStateDisplay' => true
]
'invertStateDisplay' => true,
],
],
]
],
],
'title' => [
@ -190,8 +181,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.town.unkown',
0,
'label' => $languagePath . '.town.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -207,8 +198,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.managed_by.unkown',
0,
'label' => $languagePath . '.managed_by.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -227,4 +218,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_parking_facility');
})(Extension::EXTENSION_KEY, 'tx_thuecat_parking_facility');

View file

@ -1,18 +1,21 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -27,19 +30,7 @@ return (static function (string $extensionKey, string $tableName) {
'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,
],
'config' => ['type' => 'language'],
],
'l18n_parent' => [
'displayCond' => 'FIELD:sys_language_uid:>:0',
@ -47,7 +38,7 @@ return (static function (string $extensionKey, string $tableName) {
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'items' => [['', 0]],
'items' => [['label' => '', 'value' => 0]],
'foreign_table' => $tableName,
'foreign_table_where' => 'AND ' . $tableName . '.pid=###CURRENT_PID### AND ' . $tableName . '.sys_language_uid IN (-1,0)',
'default' => 0,
@ -269,8 +260,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.town.unkown',
0,
'label' => $languagePath . '.town.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -286,8 +277,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.managed_by.unkown',
0,
'label' => $languagePath . '.managed_by.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -298,12 +289,11 @@ return (static function (string $extensionKey, string $tableName) {
'l10n_mode' => 'exclude',
'config' => [
'type' => 'group',
'internal_type' => 'db',
'allowed' => 'tx_thuecat_parking_facility',
'foreign_table' => 'tx_thuecat_parking_facility',
'suggestOptions' => [
'tx_thuecat_parking_facility' => [
'searchCondition' => 'sys_language_uid IN (0,-1)'
'searchCondition' => 'sys_language_uid IN (0,-1)',
],
],
'readOnly' => true,
@ -313,11 +303,10 @@ return (static function (string $extensionKey, string $tableName) {
'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']
),
'config' => [
'type' => 'file',
'allowed' => 'common-image-types',
],
],
],
'palettes' => [
@ -332,4 +321,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_tourist_attraction');
})(Extension::EXTENSION_KEY, 'tx_thuecat_tourist_attraction');

View file

@ -1,18 +1,21 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -52,8 +55,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.town.unkown',
0,
'label' => $languagePath . '.town.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -68,8 +71,8 @@ return (static function (string $extensionKey, string $tableName) {
'default' => '0',
'items' => [
[
$languagePath . '.managed_by.unkown',
0,
'label' => $languagePath . '.managed_by.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -82,4 +85,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_tourist_information');
})(Extension::EXTENSION_KEY, 'tx_thuecat_tourist_information');

View file

@ -1,18 +1,21 @@
<?php
declare(strict_types=1);
use WerkraumMedia\ThueCat\Extension;
defined('TYPO3') or die();
return (static function (string $extensionKey, string $tableName) {
$languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
$languagePath = Extension::getLanguagePath() . 'locallang_tca.xlf:' . $tableName;
return [
'ctrl' => [
'label' => 'title',
'iconfile' => \WerkraumMedia\ThueCat\Extension::getIconPath() . $tableName . '.svg',
'iconfile' => Extension::getIconPath() . $tableName . '.svg',
'default_sortby' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'title' => $languagePath,
'enablecolumns' => [
'disabled' => 'disable',
@ -51,8 +54,8 @@ return (static function (string $extensionKey, string $tableName) {
'foreign_table' => 'tx_thuecat_organisation',
'items' => [
[
$languagePath . '.managed_by.unkown',
0,
'label' => $languagePath . '.managed_by.unkown',
'value' => 0,
],
],
'readOnly' => true,
@ -74,4 +77,4 @@ return (static function (string $extensionKey, string $tableName) {
],
],
];
})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_town');
})(Extension::EXTENSION_KEY, 'tx_thuecat_town');

View file

@ -20,5 +20,4 @@ Table of Contents
Configuration
Integration
Changelog
Maintenance
Sitemap

View file

@ -1,15 +0,0 @@
PHP 7.4
=======
Changes that should happen once we drop PHP 7.4.
Remove ``symfony/polyfill-php80`` dependency
--------------------------------------------
We use PHP 8.0 functions within our code base (to not add legacy code and deprecations).
One example is :file:`Classes/Domain/Import/Import.php` where we use ``str_ends_with()``.
We therefore added ``symfony/polyfill-php80`` as composer package to already make use of those functions.
We can drop that package once we are at least on PHP 8.0.

View file

@ -3,130 +3,138 @@
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers"
data-namespace-typo3-fluid="true">
<h1>{f:translate(id: 'module.overview.headline')}</h1>
{f:layout(name: 'Module')}
<f:flashMessages />
<f:section name="Content">
<h1>{f:translate(id: 'module.overview.headline')}</h1>
<h2>
{f:translate(id: 'module.importConfigurations.headline')}
<f:link.newRecord
table="tx_thuecat_import_configuration"
pid="{settings.newRecordPid.tx_thuecat_import_configuration}"
title="{f:translate(id: 'module.importConfigurations.actions.new')}"
>
{f:icon(identifier: 'actions-document-add')}
</f:link.newRecord>
</h2>
<f:if condition="{importConfigurations}">
<f:then>
{f:render(section: 'ImportConfigurations', arguments: {importConfigurations: importConfigurations})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.importConfigurations.missing.title')}"
state="1"
<f:flashMessages />
<h2>
{f:translate(id: 'module.importConfigurations.headline')}
<f:link.newRecord
table="tx_thuecat_import_configuration"
pid="{settings.newRecordPid.tx_thuecat_import_configuration}"
title="{f:translate(id: 'module.importConfigurations.actions.new')}"
>
{f:translate(
id: 'module.importConfigurations.missing.text',
arguments: {
0: "{f:uri.newRecord(table: 'tx_thuecat_import_configuration', pid: settings.newRecordPid.tx_thuecat_import_configuration)}"
}
) -> f:format.raw()}
</f:be.infobox>
</f:else>
</f:if>
{f:icon(identifier: 'actions-document-add')}
</f:link.newRecord>
</h2>
<f:if condition="{importConfigurations}">
<f:then>
{f:render(section: 'ImportConfigurations', arguments: {importConfigurations: importConfigurations})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.importConfigurations.missing.title')}"
state="1"
>
{f:translate(
id: 'module.importConfigurations.missing.text',
arguments: {
0: "{f:uri.newRecord(table: 'tx_thuecat_import_configuration', pid: settings.newRecordPid.tx_thuecat_import_configuration)}"
}
) -> f:format.raw()}
</f:be.infobox>
</f:else>
</f:if>
<h2>{f:translate(id: 'module.organisations.headline')}</h2>
<f:if condition="{organisations}">
<f:then>
{f:render(section: 'Organisations', arguments: {organisations: organisations})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.organisations.missing.title')}"
state="1"
>{f:translate(id: 'module.organisations.missing.text')}</f:be.infobox>
</f:else>
</f:if>
<h2>{f:translate(id: 'module.organisations.headline')}</h2>
<f:if condition="{organisations}">
<f:then>
{f:render(section: 'Organisations', arguments: {organisations: organisations})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.organisations.missing.title')}"
state="1"
>{f:translate(id: 'module.organisations.missing.text')}</f:be.infobox>
</f:else>
</f:if>
</f:section>
<f:section name="ImportConfigurations">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{f:translate(id: 'module.importConfigurations.title')}</th>
<th>{f:translate(id: 'module.importConfigurations.lastChanged')}</th>
<th>{f:translate(id: 'module.importConfigurations.lastImported')}</th>
<th>{f:translate(id: 'module.actions')}</th>
</tr>
</thead>
<tbody>
<f:for each="{importConfigurations}" as="importConfiguration">
<div class="panel panel-default">
<table class="table table-striped table-hover">
<thead>
<tr>
<td>{importConfiguration.title}</td>
<td>{importConfiguration.lastChanged -> f:format.date(format: 'd.m.Y H:i')}</td>
<td>
<f:if condition="{importConfiguration.lastImported}">
<f:then>
{importConfiguration.lastImported -> f:format.date(format: 'd.m.Y H:i')}
</f:then>
<f:else>
{f:translate(id: 'module.importConfigurations.lastImported.never')}
</f:else>
</f:if>
</td>
<td>
<f:link.editRecord
uid="{importConfiguration.uid}"
table="{importConfiguration.tableName}"
title="{f:translate(id: 'module.importConfigurations.actions.edit')}"
>
{f:icon(identifier: 'actions-document-edit')}
</f:link.editRecord>
<f:link.action
action="import"
controller="Backend\Import"
arguments="{importConfiguration: importConfiguration}"
title="{f:translate(id: 'module.importConfigurations.actions.import')}"
>
{f:icon(identifier: 'actions-download')}
</f:link.action>
</td>
<th>{f:translate(id: 'module.importConfigurations.title')}</th>
<th>{f:translate(id: 'module.importConfigurations.lastChanged')}</th>
<th>{f:translate(id: 'module.importConfigurations.lastImported')}</th>
<th>{f:translate(id: 'module.actions')}</th>
</tr>
</f:for>
</tbody>
</table>
</thead>
<tbody>
<f:for each="{importConfigurations}" as="importConfiguration">
<tr>
<td>{importConfiguration.title}</td>
<td>{importConfiguration.lastChanged -> f:format.date(format: 'd.m.Y H:i')}</td>
<td>
<f:if condition="{importConfiguration.lastImported}">
<f:then>
{importConfiguration.lastImported -> f:format.date(format: 'd.m.Y H:i')}
</f:then>
<f:else>
{f:translate(id: 'module.importConfigurations.lastImported.never')}
</f:else>
</f:if>
</td>
<td>
<f:link.editRecord
uid="{importConfiguration.uid}"
table="{importConfiguration.tableName}"
title="{f:translate(id: 'module.importConfigurations.actions.edit')}"
>
{f:icon(identifier: 'actions-document-edit')}
</f:link.editRecord>
<f:link.action
action="import"
controller="Backend\Import"
arguments="{importConfiguration: importConfiguration}"
title="{f:translate(id: 'module.importConfigurations.actions.import')}"
>
{f:icon(identifier: 'actions-download')}
</f:link.action>
</td>
</tr>
</f:for>
</tbody>
</table>
</div>
</f:section>
<f:section name="Organisations">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{f:translate(id: 'module.organisation.title')}</th>
<th>{f:translate(id: 'module.organisation.towns')}</th>
<th>{f:translate(id: 'module.organisation.lastImported')}</th>
<th>{f:translate(id: 'module.actions')}</th>
</tr>
</thead>
<tbody>
<f:for each="{organisations}" as="organisation">
<div class="panel panel-default">
<table class="table table-striped table-hover">
<thead>
<tr>
<td>{organisation.title}</td>
<td>
{f:render(section: 'Towns', arguments: {towns: organisation.managesTowns})}
</td>
<td>{organisation.lastImported -> f:format.date(format: 'd.m.Y H:i')}</td>
<td>
<f:link.editRecord
uid="{organisation.uid}"
table="{organisation.tableName}"
>
{f:icon(identifier: 'actions-document-edit')}
</f:link.editRecord>
</td>
<th>{f:translate(id: 'module.organisation.title')}</th>
<th>{f:translate(id: 'module.organisation.towns')}</th>
<th>{f:translate(id: 'module.organisation.lastImported')}</th>
<th>{f:translate(id: 'module.actions')}</th>
</tr>
</f:for>
</tbody>
</table>
</thead>
<tbody>
<f:for each="{organisations}" as="organisation">
<tr>
<td>{organisation.title}</td>
<td>
{f:render(section: 'Towns', arguments: {towns: organisation.managesTowns})}
</td>
<td>{organisation.lastImported -> f:format.date(format: 'd.m.Y H:i')}</td>
<td>
<f:link.editRecord
uid="{organisation.uid}"
table="{organisation.tableName}"
>
{f:icon(identifier: 'actions-document-edit')}
</f:link.editRecord>
</td>
</tr>
</f:for>
</tbody>
</table>
</div>
</f:section>
<f:section name="Towns">

View file

@ -1,41 +1,47 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true">
<h1>{f:translate(id: 'module.imports.headline')}</h1>
{f:layout(name: 'Module')}
<f:flashMessages />
<f:section name="Content">
<h1>{f:translate(id: 'module.imports.headline')}</h1>
<f:if condition="{imports}">
<f:then>
{f:render(section: 'Imports', arguments: {imports: imports})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.imports.missing.title')}"
state="1"
>
{f:translate(id: 'module.imports.missing.text')}
</f:be.infobox>
</f:else>
</f:if>
<f:flashMessages />
<f:if condition="{imports}">
<f:then>
{f:render(section: 'Imports', arguments: {imports: imports})}
</f:then>
<f:else>
<f:be.infobox
title="{f:translate(id: 'module.imports.missing.title')}"
state="1"
>
{f:translate(id: 'module.imports.missing.text')}
</f:be.infobox>
</f:else>
</f:if>
</f:section>
<f:section name="Imports">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{f:translate(id: 'module.imports.th.created')}</th>
<th>{f:translate(id: 'module.imports.th.configuration')}</th>
<th>{f:translate(id: 'module.imports.th.amountOfRecords')}</th>
<th>{f:translate(id: 'module.imports.th.summary')}</th>
<th>{f:translate(id: 'module.imports.th.errors')}</th>
</tr>
</thead>
<tbody>
<f:for each="{imports}" as="import">
{f:render(section: 'Import', arguments: {import: import})}
</f:for>
</tbody>
</table>
<div class="panel panel-default">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{f:translate(id: 'module.imports.th.created')}</th>
<th>{f:translate(id: 'module.imports.th.configuration')}</th>
<th>{f:translate(id: 'module.imports.th.amountOfRecords')}</th>
<th>{f:translate(id: 'module.imports.th.summary')}</th>
<th>{f:translate(id: 'module.imports.th.errors')}</th>
</tr>
</thead>
<tbody>
<f:for each="{imports}" as="import">
{f:render(section: 'Import', arguments: {import: import})}
</f:for>
</tbody>
</table>
</div>
</f:section>
<f:section name="Import">

View file

@ -1,45 +0,0 @@
pages
,uid,pid,doktype,slug,title
,1,0,4,/,Rootpage
,2,1,255,/storage,Storage
tx_thuecat_import_configuration
,uid,pid,title,type,configuration
,1,2,"Example Configuration",static,"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes"" ?><T3FlexForms>
<data>
<sheet index=""sDEF"">
<language index=""lDEF"">
<field index=""storagePid"">
<value index=""vDEF"">2</value>
</field>
<field index=""urls"">
<el index=""el"">
<field index=""633554a57c83b383375701"">
<value index=""url"">
<el>
<field index=""url"">
<value index=""vDEF"">https://thuecat.org/resources/644315157726-jmww</value>
</field>
</el>
</value>
<value index=""_TOGGLE"">0</value>
</field>
<field index=""633551f49acee985442403"">
<value index=""url"">
<el>
<field index=""url"">
<value index=""vDEF"">https://thuecat.org/resources/072778761562-kwah</value>
</field>
</el>
</value>
<value index=""_TOGGLE"">0</value>
</field>
</el>
</field>
</language>
</sheet>
</data>
</T3FlexForms>",
"be_users"
,"uid","pid","tstamp","username","password","admin","disable","starttime","endtime","options","crdate","workspace_perms","deleted","TSconfig","lastlogin","workspace_id","db_mountpoints","usergroup","realName"
# password is "password"
,1,0,1366642540,"admin","$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1",1,0,0,0,0,1366642540,1,0,,1371033743,0,0,0,"Klaus Admin"
Can't render this file because it has a wrong number of fields in line 2.

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
return [
'be_users' => [
0 => [
'uid' => '1',
'pid' => '0',
'tstamp' => '1366642540',
'username' => 'admin',
'password' => '$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1',
'admin' => '1',
'disable' => '0',
'starttime' => '0',
'endtime' => '0',
'options' => '0',
'crdate' => '1366642540',
'workspace_perms' => '1',
'deleted' => '0',
'TSconfig' => null,
'lastlogin' => '1371033743',
'workspace_id' => '0',
],
],
'pages' => [
0 => [
'uid' => '1',
'pid' => '0',
'doktype' => PageRepository::DOKTYPE_DEFAULT,
'slug' => '/',
'title' => 'Rootpage',
],
1 => [
'uid' => '2',
'pid' => '1',
'doktype' => PageRepository::DOKTYPE_SYSFOLDER,
'slug' => '/storage',
'title' => 'Storage',
],
],
'tx_thuecat_import_configuration' => [
0 => [
'uid' => '1',
'pid' => '2',
'title' => 'Example Configuration',
'type' => 'static',
'configuration' => '<?xml version="1.0" encoding="utf-8" standalone="yes" ?><T3FlexForms>
<data>
<sheet index="sDEF">
<language index="lDEF">
<field index="storagePid">
<value index="vDEF">2</value>
</field>
<field index="urls">
<el index="el">
<field index="633554a57c83b383375701">
<value index="url">
<el>
<field index="url">
<value index="vDEF">https://thuecat.org/resources/644315157726-jmww</value>
</field>
</el>
</value>
<value index="_TOGGLE">0</value>
</field>
<field index="633551f49acee985442403">
<value index="url">
<el>
<field index="url">
<value index="vDEF">https://thuecat.org/resources/072778761562-kwah</value>
</field>
</el>
</value>
<value index="_TOGGLE">0</value>
</field>
</el>
</field>
</language>
</sheet>
</data>
</T3FlexForms>',
],
],
];

View file

@ -29,6 +29,7 @@ use WerkraumMedia\ThueCat\Tests\Acceptance\Support\_generated\AcceptanceTesterAc
/**
* Inherited Methods
*
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)

View file

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Tests\Acceptance\Support;
use Codappix\Typo3PhpDatasets\TestingFramework;
use Codeception\Event\SuiteEvent;
use TYPO3\TestingFramework\Core\Acceptance\Extension\BackendEnvironment;
/**
@ -30,6 +32,8 @@ use TYPO3\TestingFramework\Core\Acceptance\Extension\BackendEnvironment;
*/
class Environment extends BackendEnvironment
{
use TestingFramework;
protected $localConfig = [
'coreExtensionsToLoad' => [
'install',
@ -40,13 +44,17 @@ class Environment extends BackendEnvironment
'fluid',
],
'testExtensionsToLoad' => [
'typo3conf/ext/thuecat',
],
'csvDatabaseFixtures' => [
__DIR__ . '/../Data/BasicDatabase.csv',
'werkraummedia/thuecat',
],
'pathsToLinkInTestInstance' => [
'/../../../../../../Tests/Acceptance/Data/Sites/' => 'typo3conf/sites',
],
];
public function bootstrapTypo3Environment(SuiteEvent $suiteEvent)
{
parent::bootstrapTypo3Environment($suiteEvent);
$this->importPHPDataSet(__DIR__ . '/../Data/BasicDatabase.php');
}
}

View file

@ -23,14 +23,17 @@ declare(strict_types=1);
namespace WerkraumMedia\ThueCat\Tests\Functional;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
use Codappix\Typo3PhpDatasets\TestingFramework;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
abstract class AbstractImportTest extends FunctionalTestCase
abstract class AbstractImportTestCase extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
{
use TestingFramework;
/**
* Whether to expect errors to be logged.
* Will check for no errors if set to false.
*
* @var bool
*/
protected $expectErrors = false;
@ -43,15 +46,12 @@ abstract class AbstractImportTest extends FunctionalTestCase
'extbase',
'frontend',
]);
$this->testExtensionsToLoad = array_merge($this->testExtensionsToLoad, [
'typo3conf/ext/thuecat/',
'werkraummedia/thuecat/',
]);
$this->pathsToLinkInTestInstance = array_merge($this->pathsToLinkInTestInstance, [
'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Import/Sites/' => 'typo3conf/sites',
]);
$this->configurationToUseInTestInstance = array_merge($this->configurationToUseInTestInstance, [
'LOG' => [
'WerkraumMedia' => [
@ -72,12 +72,11 @@ abstract class AbstractImportTest extends FunctionalTestCase
]);
parent::setUp();
GuzzleClientFaker::registerClient();
$this->setUpBackendUserFromFixture(1);
$GLOBALS['LANG'] = $this->getContainer()->get(LanguageService::class);
$this->importPHPDataSet(__DIR__ . '/Fixtures/Import/BackendUser.php');
$this->setUpBackendUser(1);
$GLOBALS['LANG'] = $this->getContainer()->get(LanguageServiceFactory::class)->create('en_US');
foreach ($this->getLogFiles() as $logFile) {
file_put_contents($logFile, '');
}
@ -88,13 +87,8 @@ abstract class AbstractImportTest extends FunctionalTestCase
if ($this->expectErrors === true) {
return;
}
foreach ($this->getLogFiles() as $file) {
$this->assertSame(
'',
file_get_contents($file),
'The TYPO3 log file "' . $file . '" contained content while expecting to be empty.'
);
self::assertSame('', file_get_contents($file), 'The TYPO3 log file "' . $file . '" contained content while expecting to be empty.');
}
}
@ -103,10 +97,8 @@ abstract class AbstractImportTest extends FunctionalTestCase
$this->expectErrors = false;
unset($GLOBALS['LANG']);
GuzzleClientFaker::tearDown();
parent::tearDown();
}
/**
* @return string[]
*/

View file

@ -0,0 +1,195 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_import_log' => [
0 => [
'uid' => '1',
'pid' => '0',
'configuration' => '1',
],
],
'tx_thuecat_import_log_entry' => [
0 => [
'uid' => '1',
'pid' => '0',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_organisation',
'insertion' => '1',
'errors' => '[]',
],
1 => [
'uid' => '2',
'pid' => '0',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_town',
'insertion' => '1',
'errors' => '[]',
],
2 => [
'uid' => '3',
'pid' => '0',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_parking_facility',
'insertion' => '1',
'errors' => '[]',
],
3 => [
'uid' => '4',
'pid' => '0',
'import_log' => '1',
'record_uid' => '2',
'table_name' => 'tx_thuecat_parking_facility',
'insertion' => '0',
'errors' => '[]',
],
4 => [
'uid' => '5',
'pid' => '0',
'import_log' => '1',
'record_uid' => '3',
'table_name' => 'tx_thuecat_parking_facility',
'insertion' => '0',
'errors' => '[]',
],
5 => [
'uid' => '6',
'pid' => '0',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '1',
'errors' => '[]',
],
6 => [
'uid' => '7',
'pid' => '0',
'import_log' => '1',
'record_uid' => '2',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
7 => [
'uid' => '8',
'pid' => '0',
'import_log' => '1',
'record_uid' => '3',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '1',
'errors' => '[]',
],
8 => [
'uid' => '9',
'pid' => '0',
'import_log' => '1',
'record_uid' => '4',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
9 => [
'uid' => '10',
'pid' => '0',
'import_log' => '1',
'record_uid' => '5',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
],
'tx_thuecat_organisation' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/018132452787-ngbe',
'title' => 'Erfurt Tourismus und Marketing GmbH',
],
],
'tx_thuecat_town' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/043064193523-jcyt',
'title' => 'Erfurt',
'managed_by' => '1',
],
],
'tx_thuecat_parking_facility' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Parkhaus Domplatz',
'managed_by' => '1',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Car park Domplatz',
'managed_by' => '1',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Parking Domplatz',
'managed_by' => '1',
],
],
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
'managed_by' => '1',
'town' => '1',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
'managed_by' => '1',
'town' => '1',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
'managed_by' => '1',
'town' => '1',
],
3 => [
'uid' => '4',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
'managed_by' => '1',
'town' => '1',
],
4 => [
'uid' => '5',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
'managed_by' => '1',
'town' => '1',
],
],
];

View file

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
],
3 => [
'uid' => '4',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
],
4 => [
'uid' => '5',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
],
5 => [
'uid' => '6',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Krämerbrücke',
],
6 => [
'uid' => '7',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Merchants\' Bridge',
],
7 => [
'uid' => '8',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Pont de l\'épicier',
],
],
'tx_thuecat_organisation' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/018132452787-ngbe',
'title' => 'Erfurt Tourismus und Marketing GmbH',
],
],
'tx_thuecat_town' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/043064193523-jcyt',
'title' => 'Erfurt',
],
],
];

View file

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
],
],
'tx_thuecat_import_log' => [
0 => [
'uid' => '1',
'pid' => '0',
'configuration' => '1',
'log_entries' => '0',
],
],
'tx_thuecat_import_log_entry' => [
0 => [
'uid' => '1',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
1 => [
'uid' => '2',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
2 => [
'uid' => '3',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
3 => [
'uid' => '4',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_organisation',
'insertion' => '1',
'errors' => '[]',
],
4 => [
'uid' => '5',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_town',
'insertion' => '0',
'errors' => '[]',
],
5 => [
'uid' => '6',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '1',
'errors' => '[]',
],
6 => [
'uid' => '7',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '2',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
7 => [
'uid' => '8',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '3',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
],
];

View file

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '2',
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
],
],
'tx_thuecat_import_log' => [
0 => [
'uid' => '1',
'pid' => '0',
'configuration' => '1',
'log_entries' => '0',
],
],
'tx_thuecat_import_log_entry' => [
0 => [
'uid' => '1',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: DateTimeImmutable::__construct(): Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
1 => [
'uid' => '2',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: DateTimeImmutable::__construct(): Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
2 => [
'uid' => '3',
'pid' => '0',
'type' => 'mappingError',
'import_log' => '1',
'record_uid' => '0',
'table_name' => '',
'insertion' => '0',
'errors' => '["Could not map incoming JSON-LD to target object: DateTimeImmutable::__construct(): Failed to parse time string (18:00: 00) at position 5 (:): Unexpected character"]',
],
3 => [
'uid' => '4',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_organisation',
'insertion' => '1',
'errors' => '[]',
],
4 => [
'uid' => '5',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_town',
'insertion' => '0',
'errors' => '[]',
],
5 => [
'uid' => '6',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '1',
'errors' => '[]',
],
6 => [
'uid' => '7',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '2',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
7 => [
'uid' => '8',
'pid' => '0',
'type' => 'savingEntity',
'import_log' => '1',
'record_uid' => '3',
'table_name' => 'tx_thuecat_tourist_attraction',
'insertion' => '0',
'errors' => '[]',
],
],
];

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_organisation' => [
0 => [
'uid' => '1',
'pid' => '11',
'remote_id' => 'https://thuecat.org/resources/018132452787-ngbe',
'title' => 'Erfurt Tourismus und Marketing GmbH',
],
],
'tx_thuecat_import_log' => [
0 => [
'uid' => '1',
'pid' => '0',
'configuration' => '1',
],
],
'tx_thuecat_import_log_entry' => [
0 => [
'uid' => '1',
'pid' => '0',
'import_log' => '1',
'record_uid' => '1',
'table_name' => 'tx_thuecat_organisation',
'insertion' => '1',
'errors' => '[]',
],
],
];

View file

@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Domstufen 1","zip":"99084","city":"Erfurt","email":"dominformation@domberg-erfurt.de","phone":"+49 361 6461265","fax":"","geo":{"latitude":50.975955358589545,"longitude":11.023667024961856}}',
'offers' => '[]',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Domstufen 1","zip":"99084","city":"Erfurt","email":"dominformation@domberg-erfurt.de","phone":"+49 361 6461265","fax":"","geo":{"latitude":50.975955358589545,"longitude":11.023667024961856}}',
'offers' => '[]',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"F\\u00fchrungen","description":"Immer samstags, um 11:15 Uhr findet eine \\u00f6ffentliche F\\u00fchrung durch das Museum statt. Dauer etwa 90 Minuten","prices":[{"title":"Erwachsene","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"Erm\\u00e4\\u00dfigt","description":"als erm\\u00e4\\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"Eintritt","description":"Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\\nAn jedem ersten Dienstag im Monat: Eintritt frei","prices":[{"title":"Erm\\u00e4\\u00dfigt","description":"als erm\\u00e4\\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"Familienkarte","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"ErfurtCard","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"Erwachsene","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
],
3 => [
'uid' => '4',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '3',
'l10n_source' => '3',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
],
4 => [
'uid' => '5',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '3',
'l10n_source' => '3',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
],
5 => [
'uid' => '6',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Krämerbrücke',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
],
6 => [
'uid' => '7',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '6',
'l10n_source' => '6',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Merchants\' Bridge',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
],
7 => [
'uid' => '8',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '6',
'l10n_source' => '6',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Pont de l\'épicier',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
],
],
];

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'remote_id' => 'https://thuecat.org/resources/attraction-with-accessibility-specification',
'title' => 'Attraktion mit accessibility specification',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'remote_id' => 'https://thuecat.org/resources/attraction-with-accessibility-specification',
'title' => 'Attraction with accessibility specification',
],
],
];

View file

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/attraction-with-media',
'title' => 'Attraktion mit Bildern',
'media' => '[{"mainImage":false,"type":"image","title":"Bild mit externem Autor","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"GivenName FamilyName","copyrightYear":0,"license":{"type":"","author":""}},{"mainImage":false,"type":"image","title":"Bild mit author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"Full Name","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}},{"mainImage":false,"type":"image","title":"Bild mit license author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"Autor aus Lizenz"}},{"mainImage":false,"type":"image","title":"Bild mit author und license author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"Full Name","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"Autor aus Lizenz"}}]',
],
1 => [
'uid' => '2',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/attraction-with-media',
'title' => 'Attraction with media',
'media' => '[{"mainImage":false,"type":"image","title":"Bild mit externem Autor","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"GivenName FamilyName","copyrightYear":0,"license":{"type":"","author":""}},{"mainImage":false,"type":"image","title":"Bild mit author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"Full Name","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}},{"mainImage":false,"type":"image","title":"Bild mit license author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"Autor aus Lizenz"}},{"mainImage":false,"type":"image","title":"Bild mit author und license author","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"Full Name","copyrightYear":0,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"Autor aus Lizenz"}}]',
],
],
];

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
'opening_hours' => '[{"opens":"13:00:00","closes":"17:00:00","from":{"date":"2050-11-01 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"through":{"date":"2050-04-30 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"daysOfWeek":["Sunday"]}]',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
'opening_hours' => '[{"opens":"13:00:00","closes":"17:00:00","from":{"date":"2050-11-01 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"through":{"date":"2050-04-30 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"daysOfWeek":["Sunday"]}]',
],
],
];

View file

@ -0,0 +1,425 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
'description' => 'Über eine 70-stufige Freitreppe gelangt man vom Domplatz auf den Domberg mit seinen beiden ehemaligen Stiftskirchen. Der Dom, mit hochgotischem Chor, romanischem Turmbereich und spätgotischer Westhalle, ist Nachfolger des 724 von Rom veranlassten Sakralbaus. Er war die Hauptkirche des 742 von Bischof Bonifatius gegründeten Bistums Erfurt und während des Mittelalters bis in das frühe 19. Jahrhundert Sitz des Collegiatstifts St. Marien. 1507 erhielt Martin Luther hier die Priesterweihe.
Der ursprünglich romanische Kirchenbau wurde in der Zeit der Gotik entscheidend umgebaut. Besonders sehenswert sind die gotischen Chorfenster, das umfängliche Chorgestühl (14. Jhd.) sowie die romanischen Skulpturen einer thronenden Madonna und eines monumentalen Kerzenträgers im Innenraum. Berühmt ist er auch wegen der „Gloriosa“, der mit 2,56 m Durchmesser größten freischwingenden mittelalterlichen Glocke der Welt.
Das Ensemble von Dom und Severikirche bildet eine imposante Kulisse für die jährlich im Sommer stattfindenden DomStufen-Festspiele.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Domstufen 1","zip":"99084","city":"Erfurt","email":"dominformation@domberg-erfurt.de","phone":"+49 361 6461265","fax":"","geo":{"latitude":50.975955358589545,"longitude":11.023667024961856}}',
'offers' => '[]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Dom und Severikirche-beleuchtet.jpg","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5159216\\/Preview-1280x0\\/image","author":"","copyrightYear":2016,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}},{"mainImage":false,"type":"image","title":"Erfurt-Dom-und-Severikirche.jpg","description":"Sicht auf Dom St. Marien, St. Severikirche sowie die davor liegenden Klostergeb\\u00e4ude und einem Ausschnitt des Biergartens umgeben von einem d\\u00e4mmerungsverf\\u00e4rten Himmel","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5159186\\/Preview-1280x0\\/image","author":"","copyrightYear":2020,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'slogan' => '',
'start_of_construction' => '',
'sanitation' => 'Toilets,DisabledToilets',
'other_service' => 'SeatingPossibilitiesRestArea,SouvenirShop',
'museum_service' => '',
'architectural_style' => 'GothicArt',
'traffic_infrastructure' => 'BicycleLockersEnumMem,BusParkCoachParkEnumMem',
'payment_accepted' => '',
'digital_offer' => 'AugmentedReality',
'photography' => 'Fotogenehmigung für innen',
'pets_allowed' => 'false',
'is_accessible_for_free' => 'true',
'public_access' => 'true',
'available_languages' => 'German,English',
'distance_to_public_transport' => '350:MTR:Streetcar:CityBus',
'parking_facility_near_by' => '1',
'accessibility_specification' => '{}',
'url' => 'http://www.dom-erfurt.de',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
'description' => 'The late Gothic cathedral with its high-Gothic choir and Romanesque tower replaced the church built on this site for Bishop Boniface in 742. The central tower houses the "Gloriosa", the worlds largest medieval free-swinging bell.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Domstufen 1","zip":"99084","city":"Erfurt","email":"dominformation@domberg-erfurt.de","phone":"+49 361 6461265","fax":"","geo":{"latitude":50.975955358589545,"longitude":11.023667024961856}}',
'offers' => '[]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Dom und Severikirche-beleuchtet.jpg","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5159216\\/Preview-1280x0\\/image","author":"","copyrightYear":2016,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}},{"mainImage":false,"type":"image","title":"Erfurt-Dom-und-Severikirche.jpg","description":"Sicht auf Dom St. Marien, St. Severikirche sowie die davor liegenden Klostergeb\\u00e4ude und einem Ausschnitt des Biergartens umgeben von einem d\\u00e4mmerungsverf\\u00e4rten Himmel","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5159186\\/Preview-1280x0\\/image","author":"","copyrightYear":2020,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'slogan' => '',
'start_of_construction' => '',
'sanitation' => 'Toilets,DisabledToilets',
'other_service' => 'SeatingPossibilitiesRestArea,SouvenirShop',
'museum_service' => '',
'architectural_style' => 'GothicArt',
'traffic_infrastructure' => 'BicycleLockersEnumMem,BusParkCoachParkEnumMem',
'payment_accepted' => '',
'digital_offer' => 'AugmentedReality',
'photography' => '',
'pets_allowed' => 'false',
'is_accessible_for_free' => 'true',
'public_access' => 'true',
'available_languages' => 'German,English',
'distance_to_public_transport' => '350:MTR:Streetcar:CityBus',
'parking_facility_near_by' => '1',
'accessibility_specification' => '{}',
'url' => 'http://www.dom-erfurt.de',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Alte Synagoge',
'description' => 'Beispiel Beschreibung',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"F\\u00fchrungen","description":"Immer samstags, um 11:15 Uhr findet eine \\u00f6ffentliche F\\u00fchrung durch das Museum statt. Dauer etwa 90 Minuten","prices":[{"title":"Erwachsene","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"Erm\\u00e4\\u00dfigt","description":"als erm\\u00e4\\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"Eintritt","description":"Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\\nAn jedem ersten Dienstag im Monat: Eintritt frei","prices":[{"title":"Erm\\u00e4\\u00dfigt","description":"als erm\\u00e4\\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"Familienkarte","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"ErfurtCard","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"Erwachsene","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Alte Synagoge","description":"Frontaler Blick auf die Hausfront\\/Hausfassade im Innenhof mit Zugang \\u00fcber die Waagegasse","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"","copyrightYear":2009,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"F:\\\\Bilddatenbank\\\\Museen und Ausstellungen\\\\Alte Synagoge"}}]',
'slogan' => 'Highlight',
'start_of_construction' => '11. Jh.',
'sanitation' => 'Toilets,DisabledToilets,NappyChangingArea,FamilyAndChildFriendly',
'other_service' => 'SeatingPossibilitiesRestArea,LockBoxes,SouvenirShop,BaggageStorage',
'museum_service' => 'MuseumShop',
'architectural_style' => 'GothicArt',
'traffic_infrastructure' => 'ZeroSpecialTrafficInfrastructure',
'payment_accepted' => 'CashPayment,EC',
'digital_offer' => 'AudioGuide,VideoGuide',
'photography' => 'ZeroPhotography',
'pets_allowed' => 'Tiere sind im Gebäude nicht gestattet, ausgenommen sind Blinden- und Blindenbegleithunde.',
'is_accessible_for_free' => 'false',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '200:MTR:CityBus',
'parking_facility_near_by' => '',
'accessibility_specification' => '{"accessibilityCertificationStatus":"AccessibilityChecked","certificationAccessibilityDeaf":"None","certificationAccessibilityMental":"None","certificationAccessibilityPartiallyDeaf":"None","certificationAccessibilityPartiallyVisual":"Info","certificationAccessibilityVisual":"None","certificationAccessibilityWalking":"Info","certificationAccessibilityWheelchair":"Info","accessibilitySearchCriteria":{"facilityAccessibilityWalking":["AllRoomsStepFreeAccess","HingedGrabRailToilet","LateralAccessibleToilet","StepFreeAccess","ToiletsPeopleWithDisabilities","NinetyCMWidthPassageWays","SpecialOffersWalkingImpairment","SpecialOffersWheelchairUsers"],"facilityAccessibilityVisual":["AssistanceDogsWelcome","VisuallyContrastingStepEdges","OffersInPictoralLanguage","SpecialOffersBlindPeople","SpecialOffersVisualImpairment","TactileOffers"],"facilityAccessibilityDeaf":["AudioInductionLoop","SpecialOffersHearingImpairment"],"facilityAccessibilityMental":["InformationWithPictogramsOrPictures"]},"shortDescriptionAccessibilityDeaf":"Deutsche Beschreibung von shortDescriptionAccessibilityDeaf","shortDescriptionAccessibilityMental":"Deutsche Beschreibung von shortDescriptionAccessibilityMental","shortDescriptionAccessibilityVisual":"Deutsche Beschreibung von shortDescriptionAccessibilityVisual","shortDescriptionAccessibilityWalking":"Deutsche Beschreibung von shortDescriptionAccessibilityWalking"}',
'url' => 'http://www.alte-synagoge.erfurt.de',
],
3 => [
'uid' => '4',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '3',
'l10n_source' => '3',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'Old Synagogue',
'description' => 'The Old Synagogue is one of very few preserved medieval synagogues in Europe. Thanks to the extensive preservation of the original structure, it has a special place in the history of art and architecture and is among the most impressive and highly rated architectural monuments in Erfurt and Thuringia. The synagogue was constructed during the Middle Ages on the "via regia", one of the major European trade routes, at the heart of the historical old quarter very close to the Merchants Bridge and the town hall. Many parts of the structure still remain today, including all four thick outer walls, the Roman­esque gemel window, the Gothic rose window and the entrance to the synagogue room.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Alte Synagoge","description":"Frontaler Blick auf die Hausfront\\/Hausfassade im Innenhof mit Zugang \\u00fcber die Waagegasse","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"","copyrightYear":2009,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"F:\\\\Bilddatenbank\\\\Museen und Ausstellungen\\\\Alte Synagoge"}}]',
'slogan' => 'Highlight',
'start_of_construction' => '11th century',
'sanitation' => 'Toilets,DisabledToilets,NappyChangingArea,FamilyAndChildFriendly',
'other_service' => 'SeatingPossibilitiesRestArea,LockBoxes,SouvenirShop,BaggageStorage',
'museum_service' => 'MuseumShop',
'architectural_style' => 'GothicArt',
'traffic_infrastructure' => 'ZeroSpecialTrafficInfrastructure',
'payment_accepted' => 'CashPayment,EC',
'digital_offer' => 'AudioGuide,VideoGuide',
'photography' => 'ZeroPhotography',
'pets_allowed' => '',
'is_accessible_for_free' => 'false',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '200:MTR:CityBus',
'parking_facility_near_by' => '',
'accessibility_specification' => '{"accessibilityCertificationStatus":"AccessibilityChecked","certificationAccessibilityDeaf":"None","certificationAccessibilityMental":"None","certificationAccessibilityPartiallyDeaf":"None","certificationAccessibilityPartiallyVisual":"Info","certificationAccessibilityVisual":"None","certificationAccessibilityWalking":"Info","certificationAccessibilityWheelchair":"Info","accessibilitySearchCriteria":{"facilityAccessibilityWalking":["AllRoomsStepFreeAccess","HingedGrabRailToilet","LateralAccessibleToilet","StepFreeAccess","ToiletsPeopleWithDisabilities","NinetyCMWidthPassageWays","SpecialOffersWalkingImpairment","SpecialOffersWheelchairUsers"],"facilityAccessibilityVisual":["AssistanceDogsWelcome","VisuallyContrastingStepEdges","OffersInPictoralLanguage","SpecialOffersBlindPeople","SpecialOffersVisualImpairment","TactileOffers"],"facilityAccessibilityDeaf":["AudioInductionLoop","SpecialOffersHearingImpairment"],"facilityAccessibilityMental":["InformationWithPictogramsOrPictures"]},"shortDescriptionAccessibilityDeaf":"English description of shortDescriptionAccessibilityDeaf","shortDescriptionAccessibilityMental":"English description of shortDescriptionAccessibilityMental","shortDescriptionAccessibilityVisual":"English description of shortDescriptionAccessibilityVisual","shortDescriptionAccessibilityWalking":"English description of shortDescriptionAccessibilityWalking"}',
'url' => 'http://www.alte-synagoge.erfurt.de',
],
4 => [
'uid' => '5',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '3',
'l10n_source' => '3',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/165868194223-zmqf',
'title' => 'La vieille synagogue',
'description' => 'La vieille synagogue (datant des années 1100) est la synagogue la plus vieille dEurope totalement conservée, dans laquelle est exposé un trésor datant des 13/14èmes siècles avec une alliance juive unique et des écritures hébraïques (datant des 12ème, 13ème et 14èmes siècles). Après la redécouverte du Mikwé, Erfurt abrite des témoins uniques et fascinants dune communauté juive médiévale.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Waagegasse 8","zip":"99084","city":"Erfurt","email":"altesynagoge@erfurt.de","phone":"+49 361 6551520","fax":"+49 361 6551669","geo":{"latitude":50.978765,"longitude":11.029133}}',
'offers' => '[{"types":["GuidedTourOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"}]},{"types":["EntryOffer"],"title":"","description":"","prices":[{"title":"","description":"","price":5,"currency":"EUR","rule":"PerPerson"},{"title":"","description":"","price":17,"currency":"EUR","rule":"PerGroup"},{"title":"","description":"","price":14.9,"currency":"EUR","rule":"PerPackage"},{"title":"","description":"","price":8,"currency":"EUR","rule":"PerPerson"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Alte Synagoge","description":"Frontaler Blick auf die Hausfront\\/Hausfassade im Innenhof mit Zugang \\u00fcber die Waagegasse","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5099196\\/Preview-1280x0\\/image","author":"","copyrightYear":2009,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":"F:\\\\Bilddatenbank\\\\Museen und Ausstellungen\\\\Alte Synagoge"}}]',
'slogan' => 'Highlight',
'start_of_construction' => '',
'sanitation' => 'Toilets,DisabledToilets,NappyChangingArea,FamilyAndChildFriendly',
'other_service' => 'SeatingPossibilitiesRestArea,LockBoxes,SouvenirShop,BaggageStorage',
'museum_service' => 'MuseumShop',
'architectural_style' => 'GothicArt',
'traffic_infrastructure' => 'ZeroSpecialTrafficInfrastructure',
'payment_accepted' => 'CashPayment,EC',
'digital_offer' => 'AudioGuide,VideoGuide',
'photography' => 'ZeroPhotography',
'pets_allowed' => '',
'is_accessible_for_free' => 'false',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '200:MTR:CityBus',
'parking_facility_near_by' => '',
'accessibility_specification' => '{"accessibilityCertificationStatus":"AccessibilityChecked","certificationAccessibilityDeaf":"None","certificationAccessibilityMental":"None","certificationAccessibilityPartiallyDeaf":"None","certificationAccessibilityPartiallyVisual":"Info","certificationAccessibilityVisual":"None","certificationAccessibilityWalking":"Info","certificationAccessibilityWheelchair":"Info","accessibilitySearchCriteria":{"facilityAccessibilityWalking":["AllRoomsStepFreeAccess","HingedGrabRailToilet","LateralAccessibleToilet","StepFreeAccess","ToiletsPeopleWithDisabilities","NinetyCMWidthPassageWays","SpecialOffersWalkingImpairment","SpecialOffersWheelchairUsers"],"facilityAccessibilityVisual":["AssistanceDogsWelcome","VisuallyContrastingStepEdges","OffersInPictoralLanguage","SpecialOffersBlindPeople","SpecialOffersVisualImpairment","TactileOffers"],"facilityAccessibilityDeaf":["AudioInductionLoop","SpecialOffersHearingImpairment"],"facilityAccessibilityMental":["InformationWithPictogramsOrPictures"]}}',
'url' => 'http://www.alte-synagoge.erfurt.de',
],
5 => [
'uid' => '6',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Krämerbrücke',
'description' => 'Ein bekanntes Wahrzeichen Erfurts ist die Krämerbrücke, die längste bebaute und bewohnte Brücke Europas.Die Krämerbrücke wurde zu Beginn aus Holz und 1325 aus Stein erbaut. Zunächst war die 120 m lange Brücke mit 62 schmalen Häusern bebaut. Später wurden einige der Häuser auf nun mehr 32 zusammengefasst. An beiden Enden der Brücke standen zwei Brückenkopfkirchen. Heute existiert nur noch eine der beiden, die östlich gelegene Ägidienkirche.Auf der Krämerbrücke kann man in Galerien und Boutiquen sehr schön bummeln gehen. Man kann Thüringer Handwerksmeistern bei ihrer Arbeit über die Schulter schauen. Keramik, Porzellan und Holzschnitzereien, Blaudruck und Lauschaer Glas sind beliebte Souvenirs. Cafès, Weinhändler und Feinkostgeschäfte mit Thüringer Spezialitäten laden zum Verweilen ein.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Kraemerbruecke-11.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134362\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134288\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke-13.jpg","description":"Ansicht der Kr\\u00e4merbr\\u00fccke, Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/652340\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}}]',
'slogan' => '',
'start_of_construction' => '',
'sanitation' => 'ZeroSanitation',
'other_service' => 'Playground,SeatingPossibilitiesRestArea,SouvenirShop,PlayCornerOrPlayArea',
'museum_service' => '',
'architectural_style' => 'ZeroInformationArchitecturalStyle',
'traffic_infrastructure' => 'BicycleLockersEnumMem',
'payment_accepted' => '',
'digital_offer' => 'ZeroDigitalOffer',
'photography' => 'TakingPicturesPermitted',
'pets_allowed' => 'true',
'is_accessible_for_free' => 'true',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '250:MTR',
'parking_facility_near_by' => '1,4',
'accessibility_specification' => '{}',
'url' => 'https://www.erfurt-tourismus.de/sehenswertes/kraemerbruecke',
],
6 => [
'uid' => '7',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '6',
'l10n_source' => '6',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Merchants\' Bridge',
'description' => 'Another Erfurt landmark is the Merchants\' Bridge (Krämerbrücke), the longest series of inhabited buildings on any bridge in Europe. The Merchants\' Bridge is Erfurts most interesting secular construction, initially in wood but rebuilt in stone in 1325. There were originally 62 narrow buildings along its 120-metre length, but subsequent redevelopment left just 32 buildings. Of what was once a pair of bridgehead churches, only the Church of St. Aegidius remains at the eastern end of the bridge today. The Merchants\' Bridge is lined with galleries, cafés and boutiques offering traditional crafts, Thuringian blue printed fabrics, hand-painted ceramics, handblown glassware, jewellery, wood carvings, antiques and delicious Thuringian specialities - perfect for browsing.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Kraemerbruecke-11.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134362\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134288\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke-13.jpg","description":"Ansicht der Kr\\u00e4merbr\\u00fccke, Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/652340\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}}]',
'slogan' => '',
'start_of_construction' => '',
'sanitation' => 'ZeroSanitation',
'other_service' => 'Playground,SeatingPossibilitiesRestArea,SouvenirShop,PlayCornerOrPlayArea',
'museum_service' => '',
'architectural_style' => 'ZeroInformationArchitecturalStyle',
'traffic_infrastructure' => 'BicycleLockersEnumMem',
'payment_accepted' => '',
'digital_offer' => 'ZeroDigitalOffer',
'photography' => 'TakingPicturesPermitted',
'pets_allowed' => 'true',
'is_accessible_for_free' => 'true',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '250:MTR',
'parking_facility_near_by' => '1,4',
'accessibility_specification' => '{}',
'url' => 'https://www.erfurt-tourismus.de/sehenswertes/kraemerbruecke',
],
7 => [
'uid' => '8',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '6',
'l10n_source' => '6',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/215230952334-yyno',
'title' => 'Pont de l\'épicier',
'description' => 'Le pont de lépicier est un des symboles de la ville dErfurt, le plus grand pont habité en continu dEurope. A lorigine, le pont de lépicier faisait 120 m de long et comptait 62 maisons étroites, qui furent plus tard regroupées en 32 maisons. Sur le pont de lépicier se trouvent des galeries et des petites échoppes proposant des étoffes à motifs bleu indigo de Thuringe, des céramiques peintes main, du verre de Lauscha, des bijoux et des sculptures en bois.',
'managed_by' => '1',
'town' => '1',
'address' => '{"street":"Benediktsplatz 1","zip":"99084","city":"Erfurt","email":"service@erfurt-tourismus.de","phone":"+49 361 66 400","fax":"","geo":{"latitude":50.978772,"longitude":11.031622}}',
'offers' => '[]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Kraemerbruecke-11.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134362\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke.jpg","description":"Kr\\u00e4merbr\\u00fccke in Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/134288\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}},{"mainImage":false,"type":"image","title":"Erfurt-Kraemerbruecke-13.jpg","description":"Ansicht der Kr\\u00e4merbr\\u00fccke, Erfurt","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/652340\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2019,"license":{"type":"https:\\/\\/creativecommons.org\\/publicdomain\\/zero\\/1.0\\/deed.de","author":"https:\\/\\/home.ttgnet.de\\/ttg\\/projekte\\/10006\\/90136\\/Projektdokumente\\/Vergabe%20Rahmenvertrag%20Fotoproduktion"}}]',
'slogan' => '',
'start_of_construction' => '',
'sanitation' => 'ZeroSanitation',
'other_service' => 'Playground,SeatingPossibilitiesRestArea,SouvenirShop,PlayCornerOrPlayArea',
'museum_service' => '',
'architectural_style' => 'ZeroInformationArchitecturalStyle',
'traffic_infrastructure' => 'BicycleLockersEnumMem',
'payment_accepted' => '',
'digital_offer' => 'ZeroDigitalOffer',
'photography' => 'TakingPicturesPermitted',
'pets_allowed' => 'true',
'is_accessible_for_free' => 'true',
'public_access' => 'true',
'available_languages' => 'German,English,French',
'distance_to_public_transport' => '250:MTR',
'parking_facility_near_by' => '1,4',
'accessibility_specification' => '{}',
'url' => 'https://www.erfurt-tourismus.de/sehenswertes/kraemerbruecke',
],
],
'tx_thuecat_parking_facility' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Parkhaus Domplatz',
'description' => 'Das Parkhaus Domplatz befindet sich unmittelbar unterhalb der Zitadelle Petersberg am nördlichen Rand des Domplatzes. Durch die zentrale Lage ist es ein idealer Ausgangspunkt für Stadtbummel und Erkundungen des Zentrums, des Petersbergs und des Andreasviertels.',
'managed_by' => '1',
'address' => '{"street":"Bechtheimer Str. 1","zip":"99084","city":"Erfurt","email":"info@stadtwerke-erfurt.de","phone":"+49 361 5640","fax":"","geo":{"latitude":50.977648905044,"longitude":11.022127985954299}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":35,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1.5,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":10,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":50,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Parkhaus-Domplatz.jpg","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/6486108\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2021,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'ZeroSanitation',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => 'ElectricVehicleCarChargingStationEnumMem',
'payment_accepted' => '',
'distance_to_public_transport' => '240:MTR:CityBus',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Car park Domplatz',
'description' => 'The Domplatz multi-storey car park is located directly below the Petersberg Citadel on the northern edge of the Domplatz. Its central location makes it an ideal starting point for strolling through the city and exploring the centre, the Petersberg and the Andreasviertel.',
'managed_by' => '1',
'address' => '{"street":"Bechtheimer Str. 1","zip":"99084","city":"Erfurt","email":"info@stadtwerke-erfurt.de","phone":"+49 361 5640","fax":"","geo":{"latitude":50.977648905044,"longitude":11.022127985954299}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":35,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1.5,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":10,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":50,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Parkhaus-Domplatz.jpg","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/6486108\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2021,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'ZeroSanitation',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => 'ElectricVehicleCarChargingStationEnumMem',
'payment_accepted' => '',
'distance_to_public_transport' => '240:MTR:CityBus',
],
2 => [
'uid' => '3',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '1',
'l10n_source' => '1',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/396420044896-drzt',
'title' => 'Parking Domplatz',
'description' => 'Le parking à étages de la Domplatz est situé juste en dessous de la citadelle de Petersberg, sur le bord nord de la Domplatz. Son emplacement central en fait un point de départ idéal pour se promener dans la ville et explorer le centre, le Petersberg et l\'Andreasviertel.',
'managed_by' => '1',
'address' => '{"street":"Bechtheimer Str. 1","zip":"99084","city":"Erfurt","email":"info@stadtwerke-erfurt.de","phone":"+49 361 5640","fax":"","geo":{"latitude":50.977648905044,"longitude":11.022127985954299}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":35,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1.5,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":10,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":50,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Erfurt-Parkhaus-Domplatz.jpg","description":"","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/6486108\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2021,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'ZeroSanitation',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => 'ElectricVehicleCarChargingStationEnumMem',
'payment_accepted' => '',
'distance_to_public_transport' => '240:MTR:CityBus',
],
3 => [
'uid' => '4',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/440055527204-ocar',
'title' => 'Q-Park Anger 1 Parkhaus',
'description' => 'Der Q-Park liegt direkt hinter dem Kaufhaus Anger 1 im Erfurter Stadtzentrum und ist über Juri-Gagarin-Ring/Meyfartstraße zu erreichen. Durch die direkte Anbindung an den Stadtring, ist das Parkhaus gut von außerhalb über Schnellstraßen und Autobahnen zu erreichen und befindet sich gleichzeitig im unmittelbaren modernen Zentrum Erfurts.',
'managed_by' => '1',
'address' => '{"street":"Anger 1","zip":"99084","city":"Erfurt","email":"servicecenter@q-park.de","phone":"+49 218 18190290","fax":"","geo":{"latitude":50.977999330565794,"longitude":11.037503264052475}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":2.2,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":13,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Q-Park-Parkhaus-Anger1-Juri-Gagarin-Ring.JPG","description":"Stra\\u00dfenansicht des Parkhauses Q-Park am Kaufhaus Anger 1, schr\\u00e4g \\u00fcber den Juri-Gagarin-Ring","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5197164\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2020,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'Toilets',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => '',
'payment_accepted' => '',
'distance_to_public_transport' => '120:MTR',
],
4 => [
'uid' => '5',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '4',
'l10n_source' => '4',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/440055527204-ocar',
'title' => 'Q-Park Anger 1 multi-storey car park',
'description' => 'The Q-Park is located directly behind the department store Anger 1 in Erfurt\'s city centre and can be reached via Juri-Gagarin-Ring/Meyfartstraße.',
'managed_by' => '1',
'address' => '{"street":"Anger 1","zip":"99084","city":"Erfurt","email":"servicecenter@q-park.de","phone":"+49 218 18190290","fax":"","geo":{"latitude":50.977999330565794,"longitude":11.037503264052475}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":2.2,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":13,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Q-Park-Parkhaus-Anger1-Juri-Gagarin-Ring.JPG","description":"Stra\\u00dfenansicht des Parkhauses Q-Park am Kaufhaus Anger 1, schr\\u00e4g \\u00fcber den Juri-Gagarin-Ring","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5197164\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2020,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'Toilets',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => '',
'payment_accepted' => '',
'distance_to_public_transport' => '120:MTR',
],
5 => [
'uid' => '6',
'pid' => '10',
'sys_language_uid' => '2',
'l18n_parent' => '4',
'l10n_source' => '4',
'l10n_state' => null,
'remote_id' => 'https://thuecat.org/resources/440055527204-ocar',
'title' => 'Q-Park Anger 1 parking à étages',
'description' => 'Le Q-Park est situé directement derrière le grand magasin Anger 1 dans le centre-ville d\'Erfurt et peut être atteint par la Juri-Gagarin-Ring/Meyfartstraße.',
'managed_by' => '1',
'address' => '{"street":"Anger 1","zip":"99084","city":"Erfurt","email":"servicecenter@q-park.de","phone":"+49 218 18190290","fax":"","geo":{"latitude":50.977999330565794,"longitude":11.037503264052475}}',
'offers' => '[{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":2.2,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":1,"currency":"EUR","rule":"PerCar"}]},{"types":["ParkingFee"],"title":"","description":"","prices":[{"title":"","description":"","price":13,"currency":"EUR","rule":"PerCar"}]}]',
'media' => '[{"mainImage":true,"type":"image","title":"Q-Park-Parkhaus-Anger1-Juri-Gagarin-Ring.JPG","description":"Stra\\u00dfenansicht des Parkhauses Q-Park am Kaufhaus Anger 1, schr\\u00e4g \\u00fcber den Juri-Gagarin-Ring","url":"https:\\/\\/cms.thuecat.org\\/o\\/adaptive-media\\/image\\/5197164\\/Preview-1280x0\\/image","author":"Florian Trykowski","copyrightYear":2020,"license":{"type":"https:\\/\\/creativecommons.org\\/licenses\\/by\\/4.0\\/","author":""}}]',
'sanitation' => 'Toilets',
'other_service' => 'ZeroOtherServiceEnumMem',
'traffic_infrastructure' => '',
'payment_accepted' => '',
'distance_to_public_transport' => '120:MTR',
],
],
'tx_thuecat_organisation' => [
0 => [
'uid' => '1',
'pid' => '10',
'remote_id' => 'https://thuecat.org/resources/018132452787-ngbe',
'title' => 'Erfurt Tourismus und Marketing GmbH',
'description' => 'Die Erfurt Tourismus & Marketing GmbH (ETMG) wurde 1997 als offizielle Organisation zur Tourismusförderung in der Landeshauptstadt Erfurt gegründet und nahm am 01.01.1998 die Geschäftstätigkeit auf.
Zu den Aufgaben zählen die kommunale Tourismusförderung als wesentliches Instrument der Wirtschafts- und Stadtentwicklung der Landeshauptstadt Erfurt, die Positionierung der Stadt Erfurt auf dem nationalen und internationalen Tourismusmarkt als dynamische und sympathische Landeshauptstadt, attraktives Städtereiseziel und Tagungsstandort, die Vervollkommnung des touristischen Serviceangebotes entsprechend den Bedürfnissen der individuellen Gäste und der Tourismuswirtschaft und das Betreiben der Erfurt Tourist Information.
Im Januar 2009 wurde das Marketing für die Landeshauptstadt Erfurt an die ETMG übertragen und neu geordnet. Die Hauptaufgaben im Stadtmarketing liegen darin, die Wahrnehmung der Stadt in folgenden Bereichen zu verstärken: traditionsreicher und innovativer Wirtschaftsstandort, lebendiger und kreativer Wissenschaftsstandort, attraktiver Wohnstandort mit Flair und Sportstandort mit exzellenten Bedingungen für Nachwuchs- und Spitzensportler.
Gesellschafter: Stadt Erfurt
Geschäftsführerin: Frau Dr. Carmen Hildebrandt
Aufsichtsratsvorsitzender: Herr Dominik Kordon
Mitarbeiter: ca. 30 Angestellte, 4 Auszubildende',
],
],
];

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
return [
'tx_thuecat_tourist_attraction' => [
0 => [
'uid' => '1',
'pid' => '10',
'sys_language_uid' => '0',
'l18n_parent' => '0',
'l10n_source' => '0',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Dom St. Marien',
'special_opening_hours' => '[{"opens":"10:00:00","closes":"14:00:00","from":{"date":"2050-12-31 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"through":{"date":"2050-12-31 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"daysOfWeek":["Saturday"]}]',
],
1 => [
'uid' => '2',
'pid' => '10',
'sys_language_uid' => '1',
'l18n_parent' => '1',
'l10n_source' => '1',
'remote_id' => 'https://thuecat.org/resources/835224016581-dara',
'title' => 'Cathedral of St. Mary',
'special_opening_hours' => '[{"opens":"10:00:00","closes":"14:00:00","from":{"date":"2050-12-31 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"through":{"date":"2050-12-31 00:00:00.000000","timezone_type":3,"timezone":"UTC"},"daysOfWeek":["Saturday"]}]',
],
],
];

Some files were not shown because too many files have changed in this diff Show more