Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Command;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Service\GeocodeService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Command for geocoding coordinates
*/
class GeocodeCommand extends Command
{
/**
* Defines the allowed options for this command
*
* @inheritdoc
*/
protected function configure()
{
$this
->setDescription('Geocode tt_address records')
->addArgument(
'key',
InputArgument::REQUIRED,
'Google Maps key for geocoding'
);
}
/**
* Geocode all records
*
* @inheritdoc
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->getGeocodeService($input->getArgument('key'))->calculateCoordinatesForAllRecordsInTable();
return 0;
}
/**
* @param string $key Google Maps key
* @return GeocodeService
*/
protected function getGeocodeService(string $key)
{
return GeneralUtility::makeInstance(GeocodeService::class, $key);
}
}

View File

@@ -0,0 +1,295 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Controller;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Database\QueryGenerator;
use FriendsOfTYPO3\TtAddress\Domain\Model\Address;
use FriendsOfTYPO3\TtAddress\Domain\Model\Dto\Demand;
use FriendsOfTYPO3\TtAddress\Domain\Model\Dto\Settings;
use FriendsOfTYPO3\TtAddress\Domain\Repository\AddressRepository;
use FriendsOfTYPO3\TtAddress\Seo\AddressTitleProvider;
use FriendsOfTYPO3\TtAddress\Utility\CacheUtility;
use FriendsOfTYPO3\TtAddress\Utility\TypoScript;
use TYPO3\CMS\Core\Pagination\ArrayPaginator;
use TYPO3\CMS\Core\Pagination\PaginatorInterface;
use TYPO3\CMS\Core\Pagination\SimplePagination;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* AddressController
*/
class AddressController extends ActionController
{
/** @var AddressRepository */
protected $addressRepository;
/** @var QueryGenerator */
protected $queryGenerator;
/** @var Settings */
protected $extensionConfiguration;
public function initializeAction()
{
$this->queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
$this->extensionConfiguration = GeneralUtility::makeInstance(Settings::class);
}
public function showAction(Address $address = null)
{
if (is_a($address, Address::class) && ($this->settings['detail']['checkPidOfAddressRecord'] ?? false)) {
$address = $this->checkPidOfAddressRecord($address);
}
if ($address !== null) {
$provider = GeneralUtility::makeInstance(AddressTitleProvider::class);
$provider->setTitle($address, (array)($this->settings['seo']['pageTitle'] ?? []));
CacheUtility::addCacheTagsByAddressRecords([$address]);
}
$this->view->assignMultiple([
'address' => $address,
'contentObjectData' => $this->configurationManager->getContentObject()->data,
]);
return $this->htmlResponse();
}
/**
* Lists addresses by settings in waterfall principle.
* singleRecords take precedence over categories which take precedence over records from pages
*
*/
public function listAction(?array $override = [])
{
$contentData = $this->configurationManager->getContentObject()->data;
$demand = $this->createDemandFromSettings();
if (isset($contentData['first_name'], $contentData['birthday']) && (int)($this->settings['insertRecord'] ?? 0) === 1) {
$demand->setSingleRecords((string)$contentData['uid']);
}
if (!empty($override) && $this->settings['allowOverride']) {
$this->overrideDemand($demand, $override);
}
if ($demand->getSingleRecords()) {
$addresses = $this->addressRepository->getAddressesByCustomSorting($demand);
} else {
$addresses = $this->addressRepository->findByDemand($demand);
}
$paginator = $this->getPaginator($addresses);
$pagination = new SimplePagination($paginator);
// @todo remove with version 8
$this->view->assign('newPagination', true);
$this->view->assign('pagination', [
'paginator' => $paginator,
'pagination' => $pagination,
]);
$this->view->assignMultiple([
'demand' => $demand,
'addresses' => $addresses,
'contentObjectData' => $contentData,
]);
CacheUtility::addCacheTagsByAddressRecords(
$addresses instanceof QueryResultInterface ? $addresses->toArray() : $addresses
);
return $this->htmlResponse();
}
/**
* Injects the Configuration Manager and is initializing the framework settings
*
* @param ConfigurationManagerInterface $configurationManager Instance of the Configuration Manager
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
parent::injectConfigurationManager($configurationManager);
$this->configurationManager = $configurationManager;
// get the whole typoscript (_FRAMEWORK does not work anymore, don't know why)
$tsSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT,
'',
''
);
// correct the array to be in same shape like the _SETTINGS array
$tsSettings = $this->removeDots((array)($tsSettings['plugin.']['tx_ttaddress.'] ?? []));
// get original settings
// original means: what extbase does by munching flexform and TypoScript together, but leaving empty flexform-settings empty ...
$originalSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
);
$propertiesNotAllowedViaFlexForms = ['orderByAllowed'];
foreach ($propertiesNotAllowedViaFlexForms as $property) {
if (isset($tsSettings['settings'][$property])) {
$originalSettings[$property] = $tsSettings['settings'][$property];
}
}
// start override
if (isset($tsSettings['settings']['overrideFlexformSettingsIfEmpty'])) {
$typoScriptUtility = GeneralUtility::makeInstance(TypoScript::class);
$originalSettings = $typoScriptUtility->override($originalSettings, $tsSettings);
}
// Re-set global settings
$this->settings = $originalSettings;
}
protected function createDemandFromSettings(): Demand
{
$demand = new Demand();
$demand->setCategories((string)($this->settings['groups'] ?? ''));
$categoryCombination = (int)($this->settings['groupsCombination'] ?? 1) === 1 ? 'or' : 'and';
$demand->setCategoryCombination($categoryCombination);
$demand->setIncludeSubCategories((bool)($this->settings['includeSubcategories'] ?? false));
if ($this->settings['pages'] ?? false) {
$demand->setPages($this->getPidList());
}
$demand->setSingleRecords((string)($this->settings['singleRecords'] ?? ''));
$demand->setSortBy((string)($this->settings['sortBy'] ?? ''));
$demand->setSortOrder((string)($this->settings['sortOrder'] ?? ''));
$demand->setIgnoreWithoutCoordinates((bool)($this->settings['ignoreWithoutCoordinates'] ?? false));
return $demand;
}
protected function overrideDemand(Demand $demand, array $override = []): Demand
{
$ignoredValues = ['singleRecords', 'pages'];
$ignoredValuesLower = array_map('strtolower', $ignoredValues);
foreach ($ignoredValues as $property) {
unset($override[$property]);
}
// check if field exists
if (isset($override['sortBy']) && !isset($GLOBALS['TCA']['tt_address']['columns'][$override['sortBy']])) {
unset($override['sortBy']);
}
foreach ($override as $propertyName => $propertyValue) {
if (in_array(strtolower($propertyName), $ignoredValuesLower, true)) {
continue;
}
if ($propertyValue !== '' || $this->settings['allowEmptyStringsForOverwriteDemand']) {
ObjectAccess::setProperty($demand, $propertyName, $propertyValue);
}
}
return $demand;
}
/**
* @param AddressRepository $addressRepository
*/
public function injectAddressRepository(AddressRepository $addressRepository)
{
$this->addressRepository = $addressRepository;
}
/**
* Removes dots at the end of a configuration array
*
* @param array $settings the array to transformed
* @return array $settings the transformed array
*/
protected function removeDots(array $settings): array
{
$conf = [];
foreach ($settings as $key => $value) {
$conf[$this->removeDotAtTheEnd($key)] = \is_array($value) ? $this->removeDots($value) : $value;
}
return $conf;
}
/**
* Removes a dot in the end of a String
*
* @param string $string
* @return string
*/
protected function removeDotAtTheEnd($string): string
{
return preg_replace('/\.$/', '', (string)$string);
}
/**
* Retrieves subpages of given pageIds recursively until reached $this->settings['recursive']
*
* @return array an array with all pageIds
*/
protected function getPidList(): array
{
$rootPIDs = explode(',', $this->settings['pages']);
$pidList = $rootPIDs;
// iterate through root-page ids and merge to array
foreach ($rootPIDs as $pid) {
$result = $this->queryGenerator->getTreeList($pid, (int)($this->settings['recursive'] ?? 0));
if ($result) {
$subtreePids = explode(',', $result);
$pidList = array_merge($pidList, $subtreePids);
}
}
return $pidList;
}
/**
* @param QueryResultInterface|array $addresses
* @return ArrayPaginator|QueryResultPaginator
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException
*/
protected function getPaginator($addresses): PaginatorInterface
{
$currentPage = $this->request->hasArgument('currentPage') ? (int)$this->request->getArgument('currentPage') : 1;
$itemsPerPage = (int)($this->settings['paginate']['itemsPerPage'] ?? 10);
if ($itemsPerPage === 0) {
$itemsPerPage = 10;
}
if (is_array($addresses)) {
$paginator = new ArrayPaginator($addresses, $currentPage, $itemsPerPage);
} elseif ($addresses instanceof QueryResultInterface) {
$paginator = new QueryResultPaginator($addresses, $currentPage, $itemsPerPage);
} else {
throw new \RuntimeException(sprintf('Only array and query result interface allowed for pagination, given "%s"', get_class($addresses)), 1611168593);
}
return $paginator;
}
/**
* Checks if the address PID could be found in the storagePage settings of the detail plugin and
* if the pid is not found null is returned
*
* @param Address $address
* @return Address|null
*/
protected function checkPidOfAddressRecord(Address $address): ?Address
{
$allowedStoragePages = array_map('intval', $this->getPidList());
if (count($allowedStoragePages) > 0 && !in_array($address->getPid(), $allowedStoragePages, true)) {
$address = null;
}
return $address;
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Database;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Duplication of \TYPO3\CMS\Core\Database\QueryGenerator which has been deprecated
*/
class QueryGenerator
{
/**
* Recursively fetch all descendants of a given page
*
* @param int $id uid of the page
* @param int $depth
* @param int $begin
* @return string comma separated list of descendant pages
*/
public function getTreeList($id, $depth, $begin = 0): string
{
$depth = (int)$depth;
$begin = (int)$begin;
$id = (int)$id;
if ($id < 0) {
$id = abs($id);
}
if ($begin === 0) {
$theList = $id;
} else {
$theList = '';
}
if ($id && $depth > 0) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->select('uid')
->from('pages')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, Connection::PARAM_INT)),
$queryBuilder->expr()->eq('sys_language_uid', 0)
)
->orderBy('uid');
$statement = $queryBuilder->executeQuery();
while ($row = $statement->fetchAssociative()) {
if ($begin <= 0) {
$theList .= ',' . $row['uid'];
}
if ($depth > 1) {
$theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1);
if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
$theList .= ',';
}
$theList .= $theSubList;
}
}
}
return (string)$theList;
}
}

View File

@@ -0,0 +1,674 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Domain\Model;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Utility\PropertyModification;
use TYPO3\CMS\Extbase\Domain\Model\Category;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* The domain model of a Address
*/
class Address extends AbstractEntity
{
/**
* Hidden
*
* @var bool
*/
protected $hidden = false;
/**
* @var string
*/
protected $gender = '';
/**
* @var string
*/
protected $name = '';
/**
* @var string
*/
protected $firstName = '';
/**
* @var string
*/
protected $middleName = '';
/**
* @var string
*/
protected $lastName = '';
/**
* @var \DateTime|null
*/
protected $birthday;
/**
* @var string
*/
protected $title = '';
/** @var string */
protected $titleSuffix = '';
/**
* @var string
*/
protected $address = '';
/**
* @var float
*/
protected $latitude = 0;
/**
* @var float
*/
protected $longitude = 0;
/**
* @var string
*/
protected $building = '';
/**
* @var string
*/
protected $room = '';
/**
* @var string
*/
protected $phone = '';
/**
* @var string
*/
protected $fax = '';
/**
* @var string
*/
protected $mobile = '';
/**
* @var string
*/
protected $www = '';
/**
* @var string
*/
protected $slug = '';
/**
* @var string
*/
protected $skype = '';
/**
* @var string
*/
protected $twitter = '';
/**
* @var string
*/
protected $facebook = '';
/**
* @var string
*/
protected $instagram = '';
/**
* @var string
*/
protected $tiktok = '';
/**
* @var string
*/
protected $linkedin = '';
/**
* @var string
*/
protected $email = '';
/**
* @var string
*/
protected $company = '';
/**
* @var string
*/
protected $position = '';
/**
* @var string
*/
protected $city = '';
/**
* @var string
*/
protected $zip = '';
/**
* @var string
*/
protected $region = '';
/**
* @var string
*/
protected $country = '';
/**
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
*/
protected $image;
/**
* @var string
*/
protected $description = '';
/**
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<Category>
*/
protected $categories;
public function __construct()
{
$this->image = new ObjectStorage();
$this->categories = new ObjectStorage();
}
public function setHidden($hidden): void
{
$this->hidden = $hidden;
}
public function getHidden(): bool
{
return $this->hidden;
}
public function setGender(string $gender): void
{
$this->gender = $gender;
}
public function getGender(): string
{
return $this->gender;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function setFirstName(string $firstName): void
{
$this->firstName = $firstName;
}
public function getFirstName(): string
{
return $this->firstName;
}
public function setMiddleName(string $middleName): void
{
$this->middleName = $middleName;
}
public function getMiddleName(): string
{
return $this->middleName;
}
public function setLastName(string $lastName): void
{
$this->lastName = $lastName;
}
public function getLastName(): string
{
return $this->lastName;
}
public function setBirthday(?\DateTime $birthday = null): void
{
$this->birthday = $birthday;
}
public function getBirthday(): ?\DateTime
{
return $this->birthday;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getTitle(): string
{
return $this->title;
}
/**
* @return string
*/
public function getTitleSuffix(): string
{
return $this->titleSuffix;
}
/**
* @param string $titleSuffix
*/
public function setTitleSuffix(string $titleSuffix): void
{
$this->titleSuffix = $titleSuffix;
}
public function setAddress(string $address): void
{
$this->address = $address;
}
public function getAddress(): string
{
return $this->address;
}
public function setLatitude(float $latitude): void
{
$this->latitude = $latitude;
}
public function getLatitude(): float
{
return $this->latitude;
}
public function setLongitude(float $longitude): void
{
$this->longitude = $longitude;
}
public function getLongitude(): float
{
return $this->longitude;
}
public function setBuilding(string $building): void
{
$this->building = $building;
}
public function getBuilding(): string
{
return $this->building;
}
public function setRoom(string $room): void
{
$this->room = $room;
}
public function getRoom(): string
{
return $this->room;
}
public function setPhone(string $phone): void
{
$this->phone = $phone;
}
public function getPhone(): string
{
return $this->phone;
}
public function getCleanedPhone(): string
{
return PropertyModification::getCleanedNumber($this->phone);
}
public function setFax(string $fax): void
{
$this->fax = $fax;
}
public function getFax(): string
{
return $this->fax;
}
public function getCleanedFax(): string
{
return PropertyModification::getCleanedNumber($this->fax);
}
public function setMobile(string $mobile): void
{
$this->mobile = $mobile;
}
public function getMobile(): string
{
return $this->mobile;
}
public function getCleanedMobile(): string
{
return PropertyModification::getCleanedNumber($this->mobile);
}
public function setWww(string $www): void
{
$this->www = $www;
}
public function getWww(): string
{
return $this->www;
}
public function getWwwSimplified(): string
{
return PropertyModification::getCleanedDomain($this->www);
}
public function setSlug(string $slug): void
{
$this->slug = $slug;
}
public function getSlug(): string
{
return $this->slug;
}
public function setSkype(string $skype): void
{
$this->skype = $skype;
}
public function getSkype(): string
{
return $this->skype;
}
public function setTwitter(string $twitter): void
{
if ($twitter[0] !== '@') {
throw new \InvalidArgumentException('twitter name must start with @', 1357530444);
}
$this->twitter = $twitter;
}
public function getTwitter(): string
{
return $this->twitter;
}
public function setFacebook(string $facebook): void
{
if ($facebook[0] !== '/') {
throw new \InvalidArgumentException('Facebook name must start with /', 1357530471);
}
$this->facebook = $facebook;
}
public function getFacebook(): string
{
return $this->facebook;
}
public function setInstagram(string $instagram): void
{
$this->instagram = $instagram;
}
public function getInstagram(): string
{
return $this->instagram;
}
public function setTiktok(string $tiktok): void
{
$this->tiktok = $tiktok;
}
public function getTiktok(): string
{
return $this->tiktok;
}
public function setLinkedin(string $linkedin): void
{
$this->linkedin = $linkedin;
}
public function getLinkedin(): string
{
return $this->linkedin;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function getEmail(): string
{
return $this->email;
}
public function setCompany(string $company): void
{
$this->company = $company;
}
public function getCompany(): string
{
return $this->company;
}
public function setPosition(string $position): void
{
$this->position = $position;
}
public function getPosition(): string
{
return $this->position;
}
public function setCity(string $city): void
{
$this->city = $city;
}
public function getCity(): string
{
return $this->city;
}
public function setZip(string $zip): void
{
$this->zip = $zip;
}
public function getZip(): string
{
return $this->zip;
}
public function setRegion(string $region): void
{
$this->region = $region;
}
public function getRegion(): string
{
return $this->region;
}
public function setCountry(string $country): void
{
$this->country = $country;
}
public function getCountry(): string
{
return $this->country;
}
public function addImage(FileReference $image): void
{
$this->image->attach($image);
}
public function removeImage(FileReference $imageToRemove): void
{
$this->image->detach($imageToRemove);
}
/**
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
*/
public function getImage(): ?ObjectStorage
{
return $this->image;
}
public function getFirstImage(): ?FileReference
{
$images = $this->getImage();
if ($images) {
foreach ($images as $image) {
return $image;
}
}
return null;
}
/**
* @param ObjectStorage<FileReference> $image
*/
public function setImage(ObjectStorage $image): void
{
$this->image = $image;
}
public function setDescription(string $description): void
{
$this->description = $description;
}
public function getDescription(): string
{
return $this->description;
}
/**
* @return ObjectStorage<Category>
*/
public function getCategories(): ObjectStorage
{
return $this->categories;
}
/**
* @param ObjectStorage<Category> $categories
*/
public function setCategories(ObjectStorage $categories): void
{
$this->categories = $categories;
}
public function getSysLanguageUid(): int
{
return $this->_languageUid;
}
/**
* Get full name including title, first, middle and last name
*
* @return string
*/
public function getFullName(): string
{
$list = [
$this->getTitle(),
$this->getFirstName(),
$this->getMiddleName(),
$this->getLastName(),
];
$name = implode(' ', array_filter($list));
if ($this->titleSuffix) {
$name .= ', ' . $this->titleSuffix;
}
return $name;
}
/**
* Gets the first category
*
* @return \TYPO3\CMS\Extbase\Domain\Model\Category|null the first category
*/
public function getFirstCategory(): ?Category
{
$categories = $this->getCategories();
if (!is_null($categories)) {
$categories->rewind();
return $categories->current();
}
return null;
}
public function getAddressInOneLine(): string
{
return implode(', ', array_filter([
str_replace(chr(10), ',', $this->address),
$this->city,
$this->region,
$this->country,
]));
}
}

View File

@@ -0,0 +1,165 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Domain\Model\Dto;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class Demand
{
/** @var array */
protected $pages = [];
/** @var string */
protected $sortBy = '';
/** @var string */
protected $sortOrder = '';
/** @var string */
protected $categories = '';
/** @var bool */
protected $includeSubCategories = false;
/** @var string */
protected $categoryCombination = '';
/** @var string */
protected $singleRecords = '';
/** @var bool */
protected $ignoreWithoutCoordinates = false;
/**
* @return array
*/
public function getPages(): array
{
return $this->pages;
}
/**
* @param array $pages
*/
public function setPages(array $pages)
{
$this->pages = $pages;
}
/**
* @return string
*/
public function getSortBy(): string
{
return $this->sortBy;
}
/**
* @param string $sortBy
*/
public function setSortBy(string $sortBy)
{
$this->sortBy = $sortBy;
}
/**
* @return string
*/
public function getSortOrder(): string
{
return $this->sortOrder;
}
/**
* @param string $sortOrder
*/
public function setSortOrder(string $sortOrder)
{
$this->sortOrder = $sortOrder;
}
/**
* @return string
*/
public function getCategories(): string
{
return $this->categories;
}
/**
* @param string $categories
*/
public function setCategories(string $categories)
{
$this->categories = $categories;
}
/**
* @return bool
*/
public function getIncludeSubCategories(): bool
{
return $this->includeSubCategories;
}
/**
* @param bool $includeSubCategories
*/
public function setIncludeSubCategories(bool $includeSubCategories)
{
$this->includeSubCategories = $includeSubCategories;
}
/**
* @return string
*/
public function getCategoryCombination(): string
{
return $this->categoryCombination;
}
/**
* @param string $categoryCombination
*/
public function setCategoryCombination(string $categoryCombination)
{
$this->categoryCombination = $categoryCombination;
}
/**
* @return string
*/
public function getSingleRecords(): string
{
return $this->singleRecords;
}
/**
* @param string $singleRecords
*/
public function setSingleRecords(string $singleRecords)
{
$this->singleRecords = $singleRecords;
}
/**
* @return bool
*/
public function getIgnoreWithoutCoordinates(): bool
{
return $this->ignoreWithoutCoordinates;
}
/**
* @param bool $ignoreWithoutCoordinates
*/
public function setIgnoreWithoutCoordinates(bool $ignoreWithoutCoordinates)
{
$this->ignoreWithoutCoordinates = $ignoreWithoutCoordinates;
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Domain\Model\Dto;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class Settings
*/
class Settings
{
/** @var string */
protected $telephoneValidationPatternForPhp = '/[^\d\+\s\-]/';
/** @var string */
protected $telephoneValidationPatternForJs = '/[^\d\+\s\-]/g';
/** @var bool */
protected $newPagination = false;
/**
*/
public function __construct()
{
$settings = $this->getSettings();
if (!empty($settings)) {
$this->newPagination = (bool)($settings['newPagination'] ?? false);
if ($settings['telephoneValidationPatternForPhp'] ?? '') {
$this->telephoneValidationPatternForPhp = (string)$settings['telephoneValidationPatternForPhp'];
}
if ($settings['telephoneValidationPatternForJs'] ?? '') {
$this->telephoneValidationPatternForJs = (string)$settings['telephoneValidationPatternForJs'];
}
}
}
/**
* @return string
*/
public function getTelephoneValidationPatternForPhp(): string
{
return $this->telephoneValidationPatternForPhp;
}
/**
* @return string
*/
public function getTelephoneValidationPatternForJs(): string
{
return $this->telephoneValidationPatternForJs;
}
protected function getSettings(): array
{
try {
return GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('tt_address');
} catch (\Exception $e) {
return [];
}
}
}

View File

@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Domain\Repository;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Domain\Model\Dto\Demand;
use FriendsOfTYPO3\TtAddress\Service\CategoryService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* The repository for the domain model Address
*/
class AddressRepository extends Repository
{
/**
* override the storagePid settings (do not use storagePid) of extbase
*/
public function initializeObject()
{
$this->defaultQuerySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
$this->defaultQuerySettings->setRespectStoragePage(false);
}
/**
* @param Demand $demand
* @return QueryResultInterface
* @throws InvalidQueryException
*/
public function findByDemand(Demand $demand)
{
$query = $this->createDemandQuery($demand);
return $query->execute();
}
/**
* @param Demand $demand
* @return QueryInterface
* @throws InvalidQueryException
*/
protected function createDemandQuery(Demand $demand): QueryInterface
{
$query = $this->createQuery();
// sorting
$sortBy = $demand->getSortBy();
if ($sortBy && $sortBy !== 'singleSelection' && $sortBy !== 'default') {
$order = strtolower($demand->getSortOrder()) === 'desc' ? QueryInterface::ORDER_DESCENDING : QueryInterface::ORDER_ASCENDING;
$query->setOrderings([$sortBy => $order]);
}
$constraints = [];
$pages = $demand->getPages();
if (!empty($pages)) {
$constraints['pages'] = $query->in('pid', $pages);
}
$categories = $demand->getCategories();
if ($categories) {
$categoryConstraints = $this->createCategoryConstraint($query, $categories, $demand->getIncludeSubCategories());
if ($demand->getCategoryCombination() === 'or') {
$constraints['categories'] = $query->logicalOr(...$categoryConstraints);
} else {
$constraints['categories'] = $query->logicalAnd(...$categoryConstraints);
}
}
if ($demand->getIgnoreWithoutCoordinates()) {
$constraints['coordinatesLat'] = $query->logicalNot(
$query->logicalOr(
$query->equals('latitude', null),
$query->equals('latitude', 0.0)
)
);
$constraints['coordinatesLng'] = $query->logicalNot(
$query->logicalOr(
$query->equals('longitude', null),
$query->equals('longitude', 0.0)
)
);
}
if (!empty($constraints)) {
$query->matching($query->logicalAnd(...array_values($constraints)));
}
return $query;
}
/**
* Returns the database query to get the matching, see findByDemand()
*
* @param Demand $demand
* @return string
* @throws InvalidQueryException
*/
public function getSqlQuery(Demand $demand): string
{
$query = $this->createDemandQuery($demand);
$queryParser = GeneralUtility::makeInstance(Typo3DbQueryParser::class);
$queryBuilder = $queryParser->convertQueryToDoctrineQueryBuilder($query);
$queryParameters = $queryBuilder->getParameters();
$params = [];
foreach ($queryParameters as $key => $value) {
// prefix array keys with ':'
$params[':' . $key] = (\is_numeric($value)) ? $value : "'" . $value . "'"; //all non numeric values have to be quoted
unset($params[$key]);
}
// replace placeholders with real values
$query = strtr($queryBuilder->getSQL(), $params);
return $query;
}
/**
* @param Demand $demand
* @return array|QueryResultInterface
* @throws InvalidQueryException
*/
public function getAddressesByCustomSorting(Demand $demand)
{
$idList = GeneralUtility::intExplode(',', $demand->getSingleRecords(), true);
$sortBy = $demand->getSortBy();
if ($sortBy && $sortBy !== 'default' && $sortBy !== 'singleSelection') {
$query = $this->createQuery();
$order = strtolower($demand->getSortOrder()) === 'desc' ? QueryInterface::ORDER_DESCENDING : QueryInterface::ORDER_ASCENDING;
$query->setOrderings([$sortBy => $order]);
$constraints = [
$query->in('uid', $idList)
];
$query->matching($query->logicalAnd(...$constraints));
return $query->execute();
}
if ($demand->getSortOrder() === 'DESC') {
$idList = array_reverse($idList);
}
$list = [];
foreach ($idList as $id) {
$item = $this->findByIdentifier($id);
if ($item) {
$list[] = $item;
}
}
return $list;
}
/**
* Returns a category constraint created by
* a given list of categories and a junction string
*
* @param QueryInterface $query
* @param string $categories
* @param bool $includeSubCategories
* @return array
* @throws InvalidQueryException
*/
protected function createCategoryConstraint(QueryInterface $query, string $categories, bool $includeSubCategories = false): array
{
$constraints = [];
if ($includeSubCategories) {
$categoryService = GeneralUtility::makeInstance(CategoryService::class);
$allCategories = $categoryService->getChildrenCategories($categories);
if (!\is_array($allCategories)) {
$allCategories = GeneralUtility::intExplode(',', $allCategories, true);
}
} else {
$allCategories = GeneralUtility::intExplode(',', $categories, true);
}
foreach ($allCategories as $category) {
$constraints[] = $query->contains('categories', $category);
}
return $constraints;
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Evaluation;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Utility\EvalcoordinatesUtility;
/**
* Class for validation/evaluation of longitude to be used in 'eval' of TCA
* removes everything except numbers and digit-sign (dot). Fills coordinates up with zeros if too short
*/
class LatitudeEvaluation
{
/**
* Server-side validation/evaluation on saving the record
* Tests if latutide is between -90 and +90, fills up with zeros to mach decimal (14,12) in database
*
* @param string $value The field value to be evaluated
* @return string Evaluated field value
*/
public function evaluateFieldValue($value)
{
// test if we have any latitude
if ($value && $value !== '') {
return EvalcoordinatesUtility::formatLatitude($value);
}
return null;
}
/**
* Server-side validation/evaluation on opening the record
*
* @param array $parameters Array with key 'value' containing the field value from the database
* @return string Evaluated field value
*/
public function deevaluateFieldValue(array $parameters)
{
// test if we have any latitude
if ($parameters['value'] && $parameters['value'] !== '') {
$parameters['value'] = EvalcoordinatesUtility::formatLatitude($parameters['value']);
}
return $parameters['value'];
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Evaluation;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Utility\EvalcoordinatesUtility;
/**
* Class for validation/evaluation of Longitude to be used in 'eval' of TCA
* removes everything except numbers and digit-sign (dot). Fills coordinates up with zeros if too short
*/
class LongitudeEvaluation
{
/**
* Server-side validation/evaluation on saving the record
* Tests if latitude is between -90 and +90, fills up with zeros to mach decimal (14,12) in database
*
* @param string $value The field value to be evaluated
* @return string Evaluated field value
*/
public function evaluateFieldValue($value)
{
// test if we have any longitude
if ($value && $value !== '') {
return EvalcoordinatesUtility::formatLongitude($value);
}
return null;
}
/**
* Server-side validation/evaluation on opening the record
*
* @param array $parameters Array with key 'value' containing the field value from the database
* @return string Evaluated field value
*/
public function deevaluateFieldValue(array $parameters)
{
// test if we have any longitude
if ($parameters['value'] && $parameters['value'] != '') {
$parameters['value'] = EvalcoordinatesUtility::formatLongitude($parameters['value']);
}
return $parameters['value'];
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Evaluation;
use FriendsOfTYPO3\TtAddress\Domain\Model\Dto\Settings;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Class for telephone number validation/evaluation to be used in 'eval' of TCA
*/
class TelephoneEvaluation
{
/** @var Settings */
protected $extensionSettings;
public function __construct()
{
$this->extensionSettings = GeneralUtility::makeInstance(Settings::class);
}
/**
* JavaScript code for client side validation/evaluation
*/
public function returnFieldJS()
{
if ((new Typo3Version())->getMajorVersion() >= 12) {
GeneralUtility::makeInstance(PageRenderer::class)->addInlineSetting(
'TtAddress.Evaluation',
'telephoneValidationPattern',
$this->extensionSettings->getTelephoneValidationPatternForJs()
);
return JavaScriptModuleInstruction::create(
'@friendsoftypo3/tt-address/telephone-evaluation.js',
'TelephoneEvaluation'
);
}
return '
return value.replace(' . $this->extensionSettings->getTelephoneValidationPatternForJs() . ', "");
';
}
/**
* Server-side validation/evaluation on saving the record
*
* @param string $value The field value to be evaluated
* @return string Evaluated field value
*/
public function evaluateFieldValue($value)
{
return $this->evaluate($value);
}
/**
* Server-side validation/evaluation on opening the record
*
* @param array $parameters Array with key 'value' containing the field value from the database
* @return string Evaluated field value
*/
public function deevaluateFieldValue(array $parameters)
{
return $this->evaluate($parameters['value']);
}
private function evaluate(string $in)
{
$data = preg_replace($this->extensionSettings->getTelephoneValidationPatternForPhp(), '', $in);
return trim($data);
}
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types = 1);
namespace FriendsOfTYPO3\TtAddress\FormEngine\FieldControl;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
/**
* Adds a wizard for location selection via map
*/
class LocationMapWizard extends AbstractNode
{
/**
* @return array
*/
public function render(): array
{
$row = $this->data['databaseRow'];
$paramArray = $this->data['parameterArray'];
$resultArray = $this->initializeResultArray();
$nameLongitude = $paramArray['itemFormElName'];
$nameLatitude = str_replace('longitude', 'latitude', $nameLongitude);
$nameLatitudeActive = str_replace('data', 'control[active]', $nameLatitude);
$geoCodeUrl = $geoCodeUrlShort = '';
$gLat = '55.6760968';
$gLon = '12.5683371';
$lat = $row['latitude'] != '' ? htmlspecialchars($row['latitude']) : '';
$lon = $row['longitude'] != '' ? htmlspecialchars($row['longitude']) : '';
if ($row['latitude'] || $row['longitude'] == '') {
// remove all after first slash in address (top, floor ...)
$address = preg_replace('/^([^\/]*).*$/', '$1', $row['address'] ?? '') . ' ';
$address .= $row['city'] ?? '';
// if we have at least some address part (saves geocoding calls)
if ($address) {
// base url
$geoCodeUrlBase = 'https://nominatim.openstreetmap.org/search?q=';
$geoCodeUrlAddress = $address;
$geoCodeUrlCityOnly = ($row['city'] ?? '');
// urlparams for nominatim which are fixed.
$geoCodeUrlQuery = '&format=json&addressdetails=1&limit=1&polygon_svg=1';
// replace newlines with spaces; remove multiple spaces
$geoCodeUrl = trim(preg_replace('/\s\s+/', ' ', $geoCodeUrlBase . $geoCodeUrlAddress . $geoCodeUrlQuery));
$geoCodeUrlShort = trim(preg_replace('/\s\s+/', ' ', $geoCodeUrlBase . $geoCodeUrlCityOnly . $geoCodeUrlQuery));
}
}
$resultArray['iconIdentifier'] = 'location-map-wizard';
$resultArray['title'] = $this->getLanguageService()->sL('LLL:EXT:tt_address/Resources/Private/Language/locallang_db.xlf:tt_address.locationMapWizard');
$resultArray['linkAttributes']['class'] = 'locationMapWizard ';
$resultArray['linkAttributes']['data-label-title'] = $this->getLanguageService()->sL('LLL:EXT:tt_address/Resources/Private/Language/locallang_db.xlf:tt_address.locationMapWizard');
$resultArray['linkAttributes']['data-label-close'] = $this->getLanguageService()->sL('LLL:EXT:tt_address/Resources/Private/Language/locallang_db.xlf:tt_address.locationMapWizard.close');
$resultArray['linkAttributes']['data-label-import'] = $this->getLanguageService()->sL('LLL:EXT:tt_address/Resources/Private/Language/locallang_db.xlf:tt_address.locationMapWizard.import');
$resultArray['linkAttributes']['data-lat'] = $lat;
$resultArray['linkAttributes']['data-lon'] = $lon;
$resultArray['linkAttributes']['data-glat'] = $gLat;
$resultArray['linkAttributes']['data-glon'] = $gLon;
$resultArray['linkAttributes']['data-geocodeurl'] = $geoCodeUrl;
$resultArray['linkAttributes']['data-geocodeurlshort'] = $geoCodeUrlShort;
$resultArray['linkAttributes']['data-namelat'] = htmlspecialchars($nameLatitude);
$resultArray['linkAttributes']['data-namelon'] = htmlspecialchars($nameLongitude);
$resultArray['linkAttributes']['data-namelat-active'] = htmlspecialchars($nameLatitudeActive);
$resultArray['linkAttributes']['data-tiles'] = htmlspecialchars('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
$resultArray['linkAttributes']['data-copy'] = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
$resultArray['stylesheetFiles'][] = 'EXT:tt_address/Resources/Public/Contrib/leaflet-core-1.4.0.css';
$resultArray['stylesheetFiles'][] = 'EXT:tt_address/Resources/Public/Backend/LocationMapWizard/leafletBackend.css';
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
if ($versionInformation > 11) {
$id = StringUtility::getUniqueId('t3js-formengine-fieldcontrol-');
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
'TYPO3/CMS/TtAddress/leaflet-core-1.4.0'
)->instance($id);
$resultArray['requireJsModules'][] = JavaScriptModuleInstruction::forRequireJS(
'TYPO3/CMS/TtAddress/LeafletBackend'
)->instance($id);
} else {
$resultArray['requireJsModules'][] = 'TYPO3/CMS/TtAddress/leaflet-core-1.4.0';
$resultArray['requireJsModules'][] = 'TYPO3/CMS/TtAddress/LeafletBackend';
}
return $resultArray;
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\FormEngine;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Doctrine\DBAL\Connection;
use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Improve the rendering of the plugin in page module
*/
class TtAddressPreviewRenderer extends StandardContentPreviewRenderer
{
protected array $recordMapping = [
'singleRecords' => [
'table' => 'tt_address',
'multiValue' => true,
],
'pages' => [
'table' => 'pages',
'multiValue' => true,
],
'singlePid' => [
'table' => 'pages',
'multiValue' => false,
],
'groups' => [
'table' => 'sys_category',
'multiValue' => true,
],
];
protected function renderContentElementPreviewFromFluidTemplate(array $row, ?GridColumnItem $item = null): ?string
{
$row = $this->enrichRow($row);
return parent::renderContentElementPreviewFromFluidTemplate($row, $item);
}
protected function enrichRow(array $row): array
{
$settings = $this->getFlexFormData($row['pi_flexform'] ?? '');
foreach ($this->recordMapping as $fieldName => $fieldConfiguration) {
if ($settings['settings'][$fieldName] ?? false) {
$records = $this->getRecords($fieldConfiguration['table'], $settings['settings'][$fieldName]);
if ($fieldConfiguration['multiValue']) {
$row['_computed'][$fieldName] = $records;
} else {
$row['_computed'][$fieldName] = $records[0] ?: [];
}
}
}
$row['_computed']['lll'] = 'LLL:EXT:tt_address/Resources/Private/Language/ff/locallang_ff.xlf:pi1_flexform.';
return $row;
}
protected function getRecords(string $table, string $idList): array
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
$rows = $queryBuilder
->select('*')
->from($table)
->where(
$queryBuilder->expr()->in(
'uid',
$queryBuilder->createNamedParameter(GeneralUtility::intExplode(',', $idList, true), Connection::PARAM_INT_ARRAY)
)
)
->executeQuery()
->fetchAllAssociative();
foreach ($rows as &$row) {
$row['_computed']['title'] = BackendUtility::getRecordTitle($table, $row);
}
return $rows;
}
protected function getFlexFormData(string $flexforms): array
{
$settings = [];
if (!empty($flexforms)) {
$settings = GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray($flexforms);
}
return $settings;
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Hooks\Tca;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Localization\LanguageService;
/**
* Class AddFieldsToSelector
*/
class AddFieldsToSelector
{
/** @var LanguageService */
protected $languageService;
public function __construct()
{
$this->languageService = $GLOBALS['LANG'];
}
// TODO consolidate with list in pi1
const sortFields = ['gender', 'name', 'first_name', 'middle_name', 'last_name', 'title', 'company', 'address', 'building', 'room', 'birthday', 'zip', 'city', 'region', 'country', 'email', 'www', 'phone', 'mobile', 'fax'];
/**
* Manipulating the input array, $params, adding new selectorbox items.
*
* @param array $params array of select field options (reference)
*/
public function main(array &$params)
{
$selectOptions = [];
foreach (self::sortFields as $field) {
$label = $this->languageService->sL($GLOBALS['TCA']['tt_address']['columns'][$field]['label']);
$label = rtrim($label, ':');
$selectOptions[] = [
'field' => $field,
'label' => $label
];
}
// add sorting by order of single selection
$selectOptions[] = [
'field' => 'singleSelection',
'label' => $this->languageService->sL('LLL:EXT:tt_address/Resources/Private/Language/ff/locallang_ff.xlf:pi1_flexform.sortBy.singleSelection')
];
// sort by labels
$labels = [];
foreach ($selectOptions as $key => $v) {
$labels[$key] = $v['label'];
}
$labels = array_map('strtolower', $labels);
array_multisort($labels, SORT_ASC, $selectOptions);
// add fields to <select>
foreach ($selectOptions as $option) {
$params['items'][] = [
$option['label'],
$option['field']
];
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Hooks\Tca;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Dynamic label of the address record based on tsconfig
*/
class Label
{
private const FALLBACK = [
['last_name', 'first_name'],
['email'],
];
public function getAddressLabel(array &$params): void
{
if (!($params['row']['pid'] ?? 0)) {
return;
}
if (is_numeric($params['row']['uid'])) {
$row = BackendUtility::getRecordWSOL('tt_address', (int) $params['row']['uid']);
} else {
$row = $params['row'];
}
// record might be in deleting process
if (!$row) {
return;
}
$configuration = $this->getConfiguration((int) $row['pid']);
if (!$configuration) {
return;
}
foreach ($configuration as $fieldList) {
$label = [];
foreach ($fieldList as $field) {
if (isset($row['uid'], $row[$field]) && !empty($row[$field])) {
$label[] = BackendUtility::getProcessedValue('tt_address', $field, $row[$field], 0, false, 0, $row['uid']);
}
}
if (!empty($label)) {
$params['title'] = implode(', ', $label);
return;
}
}
}
protected function getConfiguration(int $pid): array
{
$labelConfiguration = BackendUtility::getPagesTSconfig($pid)['tt_address.']['label'] ?? '';
if (!$labelConfiguration) {
return self::FALLBACK;
}
$configuration = [];
$options = GeneralUtility::trimExplode(';', $labelConfiguration, true);
foreach ($options as $option) {
$configuration[] = GeneralUtility::trimExplode(',', $option, true);
}
return $configuration;
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Seo;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Domain\Model\Address;
use TYPO3\CMS\Core\PageTitle\AbstractPageTitleProvider;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Generate page title based on properties of the address model
*/
class AddressTitleProvider extends AbstractPageTitleProvider
{
private const DEFAULT_PROPERTIES = 'firstName,middleName,lastName';
private const DEFAULT_GLUE = '" "';
/**
* @param Address $address
* @param array $configuration
*/
public function setTitle(Address $address, array $configuration = []): void
{
$titleFields = [];
$fields = GeneralUtility::trimExplode(',', $configuration['properties'] ?? self::DEFAULT_PROPERTIES, true);
foreach ($fields as $field) {
$getter = 'get' . ucfirst($field);
$value = $address->$getter();
if ($value) {
$titleFields[] = $value;
}
}
if (!empty($titleFields)) {
$glue = isset($configuration['glue']) && !empty($configuration['glue']) ? $configuration['glue'] : self::DEFAULT_GLUE;
$glue = str_getcsv($glue, '');
$this->title = implode($glue[0], $titleFields);
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Service;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Service for category related stuff
*/
class CategoryService
{
/** @var TimeTracker */
protected $timeTracker;
/** @var FrontendInterface */
protected $cache;
public function __construct()
{
$this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
$cacheIdentifier = $versionInformation >= 11 ? 'ttaddress_category' : 'cache_ttaddress_category';
$this->cache = GeneralUtility::makeInstance(CacheManager::class)->getCache($cacheIdentifier);
}
/**
* Get child categories by calling recursive function
* and using the caching framework to save some queries
*
* @param string $idList list of category ids to start
* @param int $counter
* @return string comma separated list of category ids
*/
public function getChildrenCategories(string $idList, int $counter = 0)
{
$cacheIdentifier = sha1('children' . $idList);
$entry = $this->cache->get($cacheIdentifier);
if (!$entry) {
$entry = $this->getChildrenCategoriesRecursive($idList, $counter);
$this->cache->set($cacheIdentifier, $entry);
}
return $entry;
}
/**
* Get child categories
*
* @param string $idList list of category ids to start
* @param int $counter
* @return string comma separated list of category ids
*/
protected function getChildrenCategoriesRecursive(string $idList, $counter = 0): string
{
$result = [];
// add id list to the output
if ($counter === 0) {
$newList = $this->getUidListFromRecords($idList);
if ($newList) {
$result[] = $newList;
}
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$res = $queryBuilder
->select('uid')
->from('sys_category')
->where(
$queryBuilder->expr()->in('parent', $queryBuilder->createNamedParameter(explode(',', $idList), Connection::PARAM_INT_ARRAY))
)
->executeQuery();
while ($row = $res->fetchAssociative()) {
$counter++;
if ($counter > 10000) {
$this->timeTracker->setTSlogMessage('EXT:tt_address: one or more recursive categories where found');
return implode(',', $result);
}
$subcategories = $this->getChildrenCategoriesRecursive((string)$row['uid'], $counter);
$result[] = $row['uid'] . ($subcategories ? ',' . $subcategories : '');
}
$result = implode(',', $result);
return $result;
}
/**
* Fetch ids again from DB to avoid false positives
*
* @param string $idList
* @return string
*/
protected function getUidListFromRecords(string $idList): string
{
$list = [];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$rows = $queryBuilder
->select('uid')
->from('sys_category')
->where(
$queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter(explode(',', $idList), Connection::PARAM_INT_ARRAY))
)
->executeQuery()
->fetchAllAssociative();
foreach ($rows as $row) {
$list[] = $row['uid'];
}
return implode(',', $list);
}
}

View File

@@ -0,0 +1,184 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Service;
use TYPO3\CMS\Core\Cache\CacheManager;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryHelper;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Service for category related stuff
*
* thanks to https://github.com/b13/t3ext-geocoding for inspiration
*/
class GeocodeService implements SingletonInterface
{
/** @var int */
protected $cacheTime = 7776000;
/** @var string */
protected $geocodingUrl = 'https://maps.googleapis.com/maps/api/geocode/json?language=de&sensor=false';
public function __construct(string $googleMapsKey = '')
{
$this->geocodingUrl .= '&key=' . $googleMapsKey;
}
/**
* geocodes all missing records in a DB table and then stores the values
* in the DB record.
*
* only works if your DB table has the necessary fields
* helpful when calculating a batch of addresses and save the latitude/longitude automatically
*
* @param string $addWhereClause
* @return int
*/
public function calculateCoordinatesForAllRecordsInTable($addWhereClause = ''): int
{
$tableName = 'tt_address';
$latitudeField = 'latitude';
$longitudeField = 'longitude';
$streetField = 'address';
$zipField = 'zip';
$cityField = 'city';
$countryField = 'country';
// Fetch all records without latitude/longitude
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder
->select('*')
->from($tableName)
->where(
$queryBuilder->expr()->or(
$queryBuilder->expr()->isNull($latitudeField),
$queryBuilder->expr()->eq($latitudeField, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)),
$queryBuilder->expr()->eq($latitudeField, 0.00000000000),
$queryBuilder->expr()->isNull($longitudeField),
$queryBuilder->expr()->eq($longitudeField, $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)),
$queryBuilder->expr()->eq($longitudeField, 0.00000000000)
)
)
->setMaxResults(500);
if (!empty($addWhereClause)) {
$queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($addWhereClause));
}
$records = $queryBuilder->executeQuery()->fetchAllAssociative();
if (\count($records) > 0) {
foreach ($records as $record) {
$country = $record[$countryField];
// do the geocoding
if (!empty($record[$zipField]) || !empty($record[$cityField])) {
$coords = $this->getCoordinatesForAddress($record[$streetField], $record[$zipField], $record[$cityField], $country);
if ($coords) {
// Update the record to fill in the latitude and longitude values in the DB
$connection->update(
$tableName,
[
$latitudeField => $coords['latitude'],
$longitudeField => $coords['longitude'],
],
[
'uid' => $record['uid']
]
);
}
}
}
}
return \count($records);
}
/**
* core functionality: asks google for the coordinates of an address
* stores known addresses in a local cache.
*
* @param string $street
* @param string $zip
* @param string $city
* @param string $country
* @return array an array with latitude and longitude
*/
public function getCoordinatesForAddress($street = null, $zip = null, $city = null, $country = ''): array
{
$addressParts = [];
foreach ([$street, $zip . ' ' . $city, $country] as $addressPart) {
if (empty($addressPart)) {
continue;
}
$addressParts[] = trim($addressPart);
}
$address = ltrim(implode(',', $addressParts), ',');
if (empty($address)) {
return [];
}
$cacheObject = $this->initializeCache();
$cacheKey = 'geocode-' . strtolower(str_replace(' ', '-', preg_replace('/[^0-9a-zA-Z ]/m', '', $address)));
// Found in cache? Return it.
if ($cacheObject->has($cacheKey)) {
return $cacheObject->get($cacheKey);
}
$result = $this->getApiCallResult(
$this->geocodingUrl . '&address=' . urlencode($address)
);
if (empty($result) || empty($result['results']) || empty($result['results'][0]['geometry'])) {
return [];
}
$geometry = $result['results'][0]['geometry'];
$result = [
'latitude' => $geometry['location']['lat'],
'longitude' => $geometry['location']['lng'],
];
// Now store the $result in cache and return
$cacheObject->set($cacheKey, $result, [], $this->cacheTime);
return $result;
}
/**
* @param string $url
* @return array
*/
protected function getApiCallResult(string $url): array
{
$response = GeneralUtility::getUrl($url);
$result = json_decode($response, true);
if ($result['status'] !== 'OVER_QUERY_LIMIT') {
return $result;
}
return [];
}
/**
* Initializes the cache for the DB requests.
*
* @param string $name
* @return FrontendInterface Cache Object
*/
protected function initializeCache(string $name = 'ttaddress_geocoding'): FrontendInterface
{
try {
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
return $cacheManager->getCache($name);
} catch (NoSuchCacheException $e) {
throw new \RuntimeException('Unable to load Cache!', 1548785854);
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Utility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Domain\Model\Address;
/**
* Class CacheUtility handles cache tags
*/
class CacheUtility
{
/**
* Adds cache tags to page cache by tt_address-records.
*
* TYPO3 has a built-in mechanism which flushes the page cache
* when editing a record. This is done by a tag that looks
* like [table]_[record:uid]
*
* @param array $addressRecords array with address records
*/
public static function addCacheTagsByAddressRecords(array $addressRecords)
{
$prefix = 'tt_address_';
$cacheTags = [];
foreach ($addressRecords as $addressRecord) {
if (!$addressRecord instanceof Address) {
continue;
}
// cache tag for each addressRecord record
$cacheTags[] = $prefix . $addressRecord->getUid();
if ($addressRecord->_getProperty('_localizedUid') != $addressRecord->getUid()) {
$cacheTags[] = $prefix . $addressRecord->_getProperty('_localizedUid');
}
}
$GLOBALS['TSFE']->addCacheTags($cacheTags);
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Utility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Class EvalcoordinatesUtility
*/
class EvalcoordinatesUtility
{
const LATITUDE_UPPER = '90.00000000';
const LONGITUDE_UPPER = '180.00000000';
/**
* @param string $coordinate
* @return float evaluated and well-formed coordinate
*/
public static function formatLongitude(string $coordinate)
{
return self::validate($coordinate, self::LONGITUDE_UPPER);
}
/**
* @param string $coordinate
* @return float evaluated and well-formed coordinate
*/
public static function formatLatitude(string $coordinate)
{
return self::validate($coordinate, self::LATITUDE_UPPER);
}
/**
* @param string $coordinate
* @param string $upperRange
* @return string
*/
protected static function validate($coordinate, string $upperRange): string
{
if ($coordinate === '') {
return '.00000000';
}
// test if value is negative
$negative = '';
if ($coordinate[0] === '-') {
$negative = '-';
}
// remove all chars not being digits and point
// therefore we will get a number
$coordinate = preg_replace("/[^\d\.]/", '', $coordinate);
// split up string at first occurrence decimal point without losing data
$integerPart = strstr($coordinate, '.', true);
$decimalPart = strstr($coordinate, '.');
// if coordinate is given as integer (no decimal point)
if ($integerPart === false) {
$integerPart = $coordinate;
}
if ($decimalPart === false) {
$decimalPart = '00';
}
// remove all points from decimal-part
$decimalPart = preg_replace("/[^\d]/", '', $decimalPart);
// fill up with zeros or shorten to match our goal of decimal(latitude: 10,8 and longitude: 11,8) in DB
if (\strlen($decimalPart) >= 8) {
$decimalPart = substr($decimalPart, 0, 8);
} else {
$decimalPart = str_pad($decimalPart, 8, '0', STR_PAD_RIGHT);
}
// concatenate the whole string to a well-formed longitude and return
$coordinate = $integerPart . '.' . $decimalPart;
// test if value is in the possible range. longitude can be -180 to +180.
// latitude can be -90 to +90
// At this point, our minus, if there, is stored to 'negative'
// therefore we just test if integerpart is bigger than 90
if ($coordinate > $upperRange) {
$coordinate = $upperRange;
}
// reapply signed/unsigned and return
return $negative . $coordinate;
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Utility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Modify properties of the address model
*/
class PropertyModification
{
/**
* Get cleaned number of a given telephone, fax or mobile number.
* It removes all chars which are not possible to enter on your cell phone.
*
* @param string $number
* @return string
*/
public static function getCleanedNumber(string $number): string
{
$number = trim($number);
// Remove 0 on +49(0)221, but keep 0 on (0)221
if (strpos($number, '(0)') > 0) {
$number = str_replace('(0)', '', $number);
}
return preg_replace('/[^0-9#+*]/', '', $number);
}
public static function getCleanedDomain(string $domain): string
{
$domain = trim($domain);
if (!$domain) {
return '';
}
$parts = str_replace(['\\\\', '\\"'], ['\\', '"'], str_getcsv($domain, ' '));
return $parts[0];
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\Utility;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* TypoScript Utility class
*/
class TypoScript
{
/**
* @param array $previousData
* @param array $tsData
* @return array
*/
public function override(array $previousData, array $tsData)
{
$validFields = GeneralUtility::trimExplode(',', $tsData['settings']['overrideFlexformSettingsIfEmpty'], true);
foreach ($validFields as $fieldName) {
// Multilevel field
if (strpos($fieldName, '.') !== false) {
$keyAsArray = explode('.', $fieldName);
$foundInCurrentTs = $this->getValue($previousData, $keyAsArray);
if (is_string($foundInCurrentTs) && strlen($foundInCurrentTs) === 0) {
$foundInOriginal = $this->getValue($tsData['settings'], $keyAsArray);
if ($foundInOriginal) {
$previousData = $this->setValue($previousData, $keyAsArray, $foundInOriginal);
}
}
} else {
if ($fieldName === 'sortBy' && (($previousData['sortBy'] ?? '') === 'default') && (($tsData['settings']['sortBy'] ?? '') !== '')) {
unset($previousData['sortBy']);
}
// if flexform setting is empty and value is available in TS
if (((!isset($previousData[$fieldName]) || (string)$previousData[$fieldName] === '') || (strlen($previousData[$fieldName]) === 0))
&& isset($tsData['settings'][$fieldName])
) {
$previousData[$fieldName] = $tsData['settings'][$fieldName];
}
}
}
return $previousData;
}
/**
* Get value from array by path
*
* @param array $data
* @param array $path
* @return array|null
*/
protected function getValue(array $data, array $path)
{
$found = true;
for ($x = 0; $x < count($path) && $found; $x++) {
$key = $path[$x];
if (isset($data[$key])) {
$data = $data[$key];
} else {
$found = false;
}
}
if ($found) {
return $data;
}
return null;
}
/**
* Set value in array by path
*
* @param array $array
* @param $path
* @param $value
* @return array
*/
protected function setValue(array $array, $path, $value)
{
$this->setValueByReference($array, $path, $value);
return array_merge_recursive([], $array);
}
/**
* Set value by reference
*
* @param array $array
* @param array $path
* @param $value
*/
private function setValueByReference(array &$array, array $path, $value)
{
while (count($path) > 1) {
$key = array_shift($path);
$array = &$array[$key];
}
$key = reset($path);
$array[$key] = $value;
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\ViewHelpers\Clean;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Utility\PropertyModification;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class DomainViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'value');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $arguments['value'] ?: $renderChildrenClosure();
return PropertyModification::getCleanedDomain($value);
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\ViewHelpers\Clean;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Utility\PropertyModification;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class PhoneNumberViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'value');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $arguments['value'] ?: $renderChildrenClosure();
return PropertyModification::getCleanedNumber($value);
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\ViewHelpers;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class MetaTagViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('property', 'string', 'Property to be set', true);
$this->registerArgument('value', 'string', 'value');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = trim($arguments['value'] ?: $renderChildrenClosure());
if ($value) {
$property = $arguments['property'];
$metaTagManager = GeneralUtility::makeInstance(MetaTagManagerRegistry::class)->getManagerForProperty($property);
$metaTagManager->addProperty($property, $value);
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\ViewHelpers;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class RemoveSpacesViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'value');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $arguments['value'] ?: $renderChildrenClosure();
return str_replace(' ', '', $value);
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace FriendsOfTYPO3\TtAddress\ViewHelpers;
/**
* This file is part of the "tt_address" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use FriendsOfTYPO3\TtAddress\Domain\Model\Address;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class StaticGoogleMapsViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments(): void
{
$this->registerArgument('addresses', 'mixed', 'Addresses', true);
$this->registerArgument('parameters', 'array', 'Parameters', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$mapArguments = $arguments['parameters'];
$markers = [];
foreach ($arguments['addresses'] as $address) {
/** @var Address $address */
$markers[] = '&markers=' . $address->getLatitude() . ',' . $address->getLongitude();
}
if (count($markers) === 1) {
$mapArguments['zoom'] = 13;
}
return 'https://maps.googleapis.com/maps/api/staticmap?' . GeneralUtility::implodeArrayForUrl('', $mapArguments, '', true) . implode('', $markers);
}
}