diff --git a/Classes/Command/BatchCommand.php b/Classes/Command/BatchCommand.php index 1a1429d..c5f7c0a 100755 --- a/Classes/Command/BatchCommand.php +++ b/Classes/Command/BatchCommand.php @@ -16,10 +16,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface; -#[AsCommand( - name: 'nxgooglelocations:runscheduledjobs', - description: 'Run batch processing for Google Locations' -)] +#[AsCommand(name: 'nxgooglelocations:runscheduledjobs', description: 'Run batch processing for Google Locations')] class BatchCommand extends Command { public function __construct( diff --git a/Classes/Controller/ModuleController.php b/Classes/Controller/ModuleController.php index e25c495..ecf94d4 100644 --- a/Classes/Controller/ModuleController.php +++ b/Classes/Controller/ModuleController.php @@ -8,9 +8,10 @@ use Netlogix\Nxgooglelocations\Domain\Model\Batch; use Netlogix\Nxgooglelocations\Domain\Repository\BatchRepository; use Psr\Http\Message\ResponseInterface; +use RuntimeException; use SJBR\StaticInfoTables\Domain\Model\Country; use SJBR\StaticInfoTables\Domain\Repository\CountryRepository; -use TYPO3\CMS\Backend\Attribute\Controller; +use TYPO3\CMS\Backend\Attribute\AsController; use TYPO3\CMS\Backend\Template\Components\ButtonBar; use TYPO3\CMS\Backend\Template\ModuleTemplate; use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; @@ -20,13 +21,13 @@ use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\CMS\Core\Messaging\AbstractMessage; use TYPO3\CMS\Core\Type\Bitmask\Permission; +use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; -#[Controller] +#[AsController] abstract class ModuleController extends ActionController { protected array $pageRecord = []; @@ -45,11 +46,12 @@ protected function initializeAction(): void { parent::initializeAction(); - $id = (int)($this->request->getQueryParams()['id'] ?? 0); + $id = (int) ($this->request->getQueryParams()['id'] ?? 0); $this->pageRecord = BackendUtility::readPageAccess( $id, - $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW) + $this->getBackendUser() + ->getPagePermsClause(Permission::PAGE_SHOW) ) ?: []; $this->moduleTemplate = $this->moduleTemplateFactory->create($this->request); @@ -62,9 +64,8 @@ protected function initializeAction(): void $refreshButton = $buttonBar->makeLinkButton() ->setHref($this->request->getUri()) ->setTitle( - $this->getLanguageService()->sL( - 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload' - ) + $this->getLanguageService() + ->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload') ) ->setIcon($this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL)); @@ -74,6 +75,7 @@ protected function initializeAction(): void protected function getModuleTemplateResponse(): ResponseInterface { $this->moduleTemplate->setContent($this->view->render()); + return $this->htmlResponse($this->moduleTemplate->renderContent()); } @@ -117,11 +119,15 @@ public function indexAction(int $id): ResponseInterface return $this->getModuleTemplateResponse(); } - public function importAction(int $id, bool $deleteUnused, bool $cancelPrevious, Country $country = null): ResponseInterface - { + public function importAction( + int $id, + bool $deleteUnused, + bool $cancelPrevious, + Country $country = null + ): ResponseInterface { $file = $this->request->getUploadedFiles()['excelFile'] ?? null; if ($file === null) { - throw new \RuntimeException('Uploading file failed.', 1702385736); + throw new RuntimeException('Uploading file failed.', 1702385736); } $batch = $this->mapRequestToBatch($id, $file, $deleteUnused, $cancelPrevious, $country); @@ -129,7 +135,12 @@ public function importAction(int $id, bool $deleteUnused, bool $cancelPrevious, try { $batch->validate(); } catch (Exception $exception) { - $this->addFlashMessage($exception->getMessage(), '', \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR); + $this->addFlashMessage( + $exception->getMessage(), + '', + ContextualFeedbackSeverity::ERROR + ); + return $this->redirect('index'); } @@ -154,10 +165,11 @@ public function importAction(int $id, bool $deleteUnused, bool $cancelPrevious, $this->addFlashMessage( LocalizationUtility::translate('module.flash-messages.new-job-scheduled.content', $extensionName) ); + return $this->redirect('index'); } - public function exportAction(): ResponseInterface + public function exportAction(int $id): ResponseInterface { return $this->htmlResponse(); } @@ -174,8 +186,9 @@ protected function forwardToErrorWithCannedMessage(string $reason): ResponseInte $this->addFlashMessage( LocalizationUtility::translate(sprintf('module.flash-messages.%s.content', $reason), $extensionName), LocalizationUtility::translate(sprintf('module.flash-messages.%s.title', $reason), $extensionName), - \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR + ContextualFeedbackSeverity::ERROR ); + return $this->redirect('error'); } diff --git a/Classes/Domain/Model/Batch.php b/Classes/Domain/Model/Batch.php index e3c6a16..fb2c808 100644 --- a/Classes/Domain/Model/Batch.php +++ b/Classes/Domain/Model/Batch.php @@ -158,12 +158,14 @@ protected function collectTcaRecords(): array return $tcaRecords; } - protected function executeDataHandler(array $tcaRecords): void + protected function executeDataHandler(array $tcaRecords): array { $backendUserId = $this->backendUserId; $storagePageId = $this->storagePageId; $deleteUnused = $this->deleteUnused; + $recordUids = []; + $importer = $this->getImporter(); $recordTableName = $this->getLocationFactory() @@ -171,13 +173,22 @@ protected function executeDataHandler(array $tcaRecords): void $this->impersonator->runAsBackendUser( $backendUserId, - static function () use ($importer, $recordTableName, $tcaRecords, $storagePageId, $deleteUnused): void { + static function () use ( + $importer, + $recordTableName, + $tcaRecords, + $storagePageId, + $deleteUnused, + &$recordUids + ): void { $recordUids = $importer->import($recordTableName, $storagePageId, $tcaRecords); if ($deleteUnused) { $importer->removeRecordsExcept($recordTableName, $storagePageId, $recordUids); } } ); + + return $recordUids; } protected function mapTcaRecord($tcaRecord): array @@ -249,7 +260,10 @@ protected function initializeServices(): void } if (!$this->importer instanceof \Netlogix\Nxgooglelocations\Service\Importer) { - $this->importer = GeneralUtility::makeInstance($this->serviceClasses[Importer::class], $this->storagePageId); + $this->importer = GeneralUtility::makeInstance( + $this->serviceClasses[Importer::class], + $this->storagePageId + ); } if (!$this->locationFactory instanceof \Netlogix\Nxgooglelocations\Service\LocationFactory) { diff --git a/Classes/Domain/Model/CodingResult.php b/Classes/Domain/Model/CodingResult.php index c530b60..6fa9f4b 100644 --- a/Classes/Domain/Model/CodingResult.php +++ b/Classes/Domain/Model/CodingResult.php @@ -5,7 +5,7 @@ namespace Netlogix\Nxgooglelocations\Domain\Model; use Netlogix\Nxgooglelocations\Enumerations\CodingResultProbability; -use Netlogix\Nxgooglelocations\Service\GeoCoderStatus; +use Netlogix\Nxgooglelocations\Enumerations\GeoCoderStatus; use TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException; use TYPO3\CMS\Extbase\Reflection\ObjectAccess; @@ -36,13 +36,13 @@ public function __get($propertyName) { return match ($propertyName) { 'rawData' => $this->rawData, - 'status' => (string)ObjectAccess::getPropertyPath($this->rawData, 'status'), - 'formattedAddress', 'addressResultFromGeocoding' => (string)ObjectAccess::getPropertyPath( + 'status' => (string) ObjectAccess::getPropertyPath($this->rawData, 'status'), + 'formattedAddress', 'addressResultFromGeocoding' => (string) ObjectAccess::getPropertyPath( $this->rawData, 'results.0.formatted_address' ), - 'latitude' => (float)ObjectAccess::getPropertyPath($this->rawData, 'results.0.geometry.location.lat'), - 'longitude' => (float)ObjectAccess::getPropertyPath($this->rawData, 'results.0.geometry.location.lng'), + 'latitude' => (float) ObjectAccess::getPropertyPath($this->rawData, 'results.0.geometry.location.lat'), + 'longitude' => (float) ObjectAccess::getPropertyPath($this->rawData, 'results.0.geometry.location.lng'), 'position' => [ 'latitude' => $this->latitude, 'longitude' => $this->longitude, diff --git a/Classes/Domain/Model/FieldMap.php b/Classes/Domain/Model/FieldMap.php index 0d874a2..90b55d3 100644 --- a/Classes/Domain/Model/FieldMap.php +++ b/Classes/Domain/Model/FieldMap.php @@ -27,7 +27,7 @@ abstract class FieldMap 'probability' => 'tx_nxgooglelocations_probability', ]; - public function __get($propertyName): string + public function __get($propertyName): ?string { return $this->fieldMap[$propertyName]; } diff --git a/Classes/Domain/Repository/BatchRepository.php b/Classes/Domain/Repository/BatchRepository.php index eda8a13..44e047e 100644 --- a/Classes/Domain/Repository/BatchRepository.php +++ b/Classes/Domain/Repository/BatchRepository.php @@ -4,11 +4,11 @@ namespace Netlogix\Nxgooglelocations\Domain\Repository; -use Netlogix\Nxgooglelocations\Enumerations\BatchState; -use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use Netlogix\Nxgooglelocations\Domain\Model\Batch; +use Netlogix\Nxgooglelocations\Enumerations\BatchState; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface; +use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; use TYPO3\CMS\Extbase\Persistence\Repository; @@ -39,6 +39,8 @@ public function findOpenInFolder(int $storagePageId): QueryResultInterface public function findOneByState(string $state): ?Batch { - return $this->findOneBy(['state' => $state]); + return $this->findOneBy([ + 'state' => $state, + ]); } } diff --git a/Classes/Enumerations/BatchState.php b/Classes/Enumerations/BatchState.php index 3ca93bb..02f1c64 100644 --- a/Classes/Enumerations/BatchState.php +++ b/Classes/Enumerations/BatchState.php @@ -33,6 +33,11 @@ final class BatchState extends Enumeration */ public const PERSISTING = 'persisting'; + /** + * @var string + */ + public const ERROR = 'error'; + /** * @var string */ diff --git a/Classes/Service/GeoCoder.php b/Classes/Service/GeoCoder.php index 4a2b18e..550bb57 100644 --- a/Classes/Service/GeoCoder.php +++ b/Classes/Service/GeoCoder.php @@ -14,7 +14,6 @@ abstract class GeoCoder { - final public const FETCH_URL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'; protected FieldMap $fieldMap; @@ -35,12 +34,13 @@ public function __construct( public function getGeoCodingAddress(array $tcaRecord): string { - return $tcaRecord[$this->fieldMap->addressToGeocode] ?: $tcaRecord[$this->fieldMap->addressToDisplay]; + return $tcaRecord[$this->fieldMap->addressToGeocode] ?? $tcaRecord[$this->fieldMap->addressToDisplay] ?? ''; } public function needsToBeGeoCoded(array $tcaRecord): bool { - return (!$tcaRecord[$this->fieldMap->latitude] && !$tcaRecord[$this->fieldMap->longitude]) || ($tcaRecord[$this->fieldMap->probability] > $this->probabilityThreshold); + return (!($tcaRecord[$this->fieldMap->latitude] ?? false) && !($tcaRecord[$this->fieldMap->longitude] ?? false)) + || (($tcaRecord[$this->fieldMap->probability] ?? PHP_INT_MAX) > $this->probabilityThreshold); } public function setProbabilityToManually(array $tcaRecord): array @@ -58,13 +58,14 @@ public function fetchCoordinatesForAddress($address): CodingResult $urlWithApiKey = sprintf(self::FETCH_URL, urlencode((string) $address), urlencode($this->apiKey)); $geocode = json_decode((string) GeneralUtility::getUrl($urlWithApiKey), true, 512, JSON_THROW_ON_ERROR); $status = ObjectAccess::getPropertyPath($geocode, 'status'); + return match ($status) { GeoCoderStatus::OK, GeoCoderStatus::ZERO_RESULTS => new CodingResult($geocode), default => throw new Exception( 'An error occurred: ' . json_encode( array_filter( [$status, ObjectAccess::getPropertyPath($geocode, 'error_message')], - static fn($value): bool => (bool)$value + static fn ($value): bool => (bool) $value ), JSON_THROW_ON_ERROR ) diff --git a/Classes/Service/Importer.php b/Classes/Service/Importer.php index 14b541f..bfd4d72 100644 --- a/Classes/Service/Importer.php +++ b/Classes/Service/Importer.php @@ -43,7 +43,9 @@ public function import(string $recordTableName, int $storagePageId, array $tcaRe $count = 0; foreach ($tcaRecords as $tcaRecord) { ++$count; - $uid = array_key_exists('uid', $tcaRecord) ? $tcaRecord['uid'] : sprintf('NEW%s', substr(md5(self::class . $count), 0, 10)); + $uid = array_key_exists('uid', $tcaRecord) + ? $tcaRecord['uid'] + : sprintf('NEW%s', substr(md5(self::class . $count), 0, 10)); $data[$recordTableName][$uid] = $tcaRecord; $data[$recordTableName][$uid]['pid'] = $storagePageId; $data[$recordTableName][$uid]['sys_language_uid'] = -1; diff --git a/Classes/ViewHelpers/Be/TableListViewHelper.php b/Classes/ViewHelpers/Be/TableListViewHelper.php index 983c368..45fd3ca 100644 --- a/Classes/ViewHelpers/Be/TableListViewHelper.php +++ b/Classes/ViewHelpers/Be/TableListViewHelper.php @@ -1,5 +1,7 @@ getParsedBody()['table'] ?? $request->getQueryParams()['table'] ?? ''; $preventPointer = $tableName !== $table; - $this->getPageRenderer()->loadJavaScriptModule('@typo3/backend/recordlist.js'); + $this->getPageRenderer() + ->loadJavaScriptModule('@typo3/backend/recordlist.js'); // Removed to disable the download button // $this->getPageRenderer()->loadJavaScriptModule('@typo3/backend/record-download-button.js'); - $this->getPageRenderer()->loadJavaScriptModule('@typo3/backend/action-dispatcher.js'); + $this->getPageRenderer() + ->loadJavaScriptModule('@typo3/backend/action-dispatcher.js'); if ($enableControlPanels === true) { - $this->getPageRenderer()->loadJavaScriptModule('@typo3/backend/multi-record-selection.js'); - $this->getPageRenderer()->loadJavaScriptModule('@typo3/backend/context-menu.js'); + $this->getPageRenderer() + ->loadJavaScriptModule('@typo3/backend/multi-record-selection.js'); + $this->getPageRenderer() + ->loadJavaScriptModule('@typo3/backend/context-menu.js'); } - $pageId = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0); + $pageId = (int) ($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0); // Added to fix the issue with the table pointer $pointer = $preventPointer ? 0 - : (int)($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0); + : (int) ($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0); $pageInfo = BackendUtility::readPageAccess( $pageId, $backendUser->getPagePermsClause(Permission::PAGE_SHOW) @@ -183,10 +189,13 @@ protected function render(): string $dbList->start($storagePid, $tableName, $pointer, $filter, $levels, $recordsPerPage); // Column selector is disabled since fields are defined by the "fieldList" argument $dbList->displayColumnSelector = false; - $dbList->setFields = [$tableName => $fieldList]; + $dbList->setFields = [ + $tableName => $fieldList, + ]; $dbList->noControlPanels = !$enableControlPanels; $dbList->sortField = $sortField; $dbList->sortRev = $sortDescending; + return $dbList->generateList(); } diff --git a/Configuration/Icons.php b/Configuration/Icons.php index 5842444..42b19e8 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -1,9 +1,11 @@ [ + 'ext-nxgooglelocations-batch-type-default' => [ 'provider' => SvgIconProvider::class, 'source' => 'EXT:nxgooglelocations/Resources/Public/Icons/tx_nxgooglelocations_domain_model_batch.svg', ],