From a605ecfb3dc8fbeac96b8027385c53dbba8a6503 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Fri, 16 Jun 2023 15:47:47 +0200 Subject: [PATCH 01/15] [TASK][WIP] Support TYPO3 12.4 --- Classes/Controller/LogController.php | 66 ++++++++++++------- Classes/Domain/Model/Log.php | 2 +- Classes/Domain/Repository/LogRepository.php | 2 +- .../Domain/Repository/StorageRepository.php | 4 +- .../SecureDownloadsEventListener.php | 28 ++------ Classes/Factory/SecureLinkFactory.php | 34 ++++++++-- Classes/Middleware/FileDeliveryMiddleware.php | 2 +- Classes/Middleware/TokenRefreshMiddleware.php | 5 +- Classes/Resource/FileDelivery.php | 14 ++-- Classes/UserFunctions/CheckConfiguration.php | 30 ++++----- Configuration/Services.yaml | 3 + .../tx_securedownloads_domain_model_log.php | 1 - .../vendor/composer/InstalledVersions.php | 2 +- .../vendor/composer/autoload_classmap.php | 24 ++++--- Libraries/vendor/composer/autoload_real.php | 11 ++-- Libraries/vendor/composer/autoload_static.php | 24 ++++--- Migrations/Code/ClassAliasMap.php | 30 ++++----- composer.json | 10 +-- ext_emconf.php | 6 +- ext_localconf.php | 2 +- ext_tables.php | 2 +- 21 files changed, 172 insertions(+), 130 deletions(-) diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index f031052..99906d5 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -14,12 +14,15 @@ * ***/ +use Doctrine\DBAL\Exception; use Leuchtfeuer\SecureDownloads\Domain\Repository\LogRepository; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Template\Components\Menu\Menu; +use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Backend\View\BackendTemplateView; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Pagination\ArrayPaginator; @@ -27,31 +30,33 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; -use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; -use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; class LogController extends ActionController { const FILTER_SESSION_KEY = 'sdl-filter'; + /** - * @var BackendTemplateView + * @var LogRepository $logRepository */ - protected $view; - - protected $defaultViewObjectName = BackendTemplateView::class; + protected LogRepository $logRepository; - protected $logRepository; + /** + * @var ModuleTemplateFactory + */ + protected ModuleTemplateFactory $moduleTemplateFactory; - public function __construct(LogRepository $logRepository) + public function __construct(LogRepository $logRepository, ModuleTemplateFactory $moduleTemplateFactory) { $this->logRepository = $logRepository; + $this->moduleTemplateFactory = $moduleTemplateFactory; } /** + * @return ResponseInterface * @throws NoSuchArgumentException */ - public function initializeAction(): void + public function initializeAction(): ResponseInterface { parent::initializeAction(); @@ -66,12 +71,17 @@ public function initializeAction(): void if ($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY) === null) { $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter())); } + $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); + $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); + return $this->htmlResponse(''); } /** * @param Filter|null $filter The filter object + * @return ResponseInterface + * @throws Exception */ - public function listAction(?Filter $filter = null): void + public function listAction(?Filter $filter = null): ResponseInterface { $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); $filter->setPageId(0); @@ -96,10 +106,15 @@ public function listAction(?Filter $filter = null): void 'pagination' => $pagination, 'totalResultCount' => count($logEntries), ]); + $moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $this->createMenu(); + $moduleTemplate->setContent($this->view->render()); + return $this->htmlResponse($moduleTemplate->renderContent()); } /** * @return array Array containing all users that have downloaded files + * @throws Exception */ private function getUsers(): array { @@ -111,12 +126,13 @@ private function getUsers(): array ->join('log', 'fe_users', 'users', $queryBuilder->expr()->eq('users.uid', 'log.user')) ->where($queryBuilder->expr()->neq('user', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))) ->groupBy('users.uid') - ->execute() + ->executeQuery() ->fetchAll(); } /** * @return array Array containing all used file types + * @throws Exception */ private function getFileTypes(): array { @@ -125,22 +141,22 @@ private function getFileTypes(): array return $queryBuilder ->select('media_type') ->from('tx_securedownloads_domain_model_log') - ->groupBy('media_type') - ->orderBy('media_type', 'ASC') - ->execute() + ->groupBy('media_type')->orderBy('media_type', 'ASC') + ->executeQuery() ->fetchAll(); } /** * @param Filter|null $filter The filter object - * @throws StopActionException + * @return ResponseInterface + * @throws Exception */ - public function showAction(?Filter $filter = null): void + public function showAction(?Filter $filter = null): ResponseInterface { $pageId = (int)GeneralUtility::_GP('id'); if ($pageId === 0) { - $this->redirect('list'); + return $this->redirect('list'); } $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); @@ -167,15 +183,18 @@ public function showAction(?Filter $filter = null): void 'pagination' => $pagination, 'totalResultCount' => count($logEntries), ]); + + $moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $this->createMenu(); + $moduleTemplate->setContent($this->view->render()); + return $this->htmlResponse($moduleTemplate->renderContent()); } /** * Set up the doc header properly here */ - public function initializeView(ViewInterface $view): void + public function initializeView(): void { - parent::initializeView($view); - $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); $this->createMenu(); @@ -186,7 +205,8 @@ public function initializeView(ViewInterface $view): void */ private function createMenu(): void { - $menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); + $moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); $menu->setIdentifier('secure_downloads'); if ((int)GeneralUtility::_GP('id') !== 0) { @@ -194,7 +214,7 @@ private function createMenu(): void } $this->view->assign('action', $this->request->getControllerActionName()); - $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); + $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); } /** diff --git a/Classes/Domain/Model/Log.php b/Classes/Domain/Model/Log.php index 6847fd5..95dda70 100644 --- a/Classes/Domain/Model/Log.php +++ b/Classes/Domain/Model/Log.php @@ -84,7 +84,7 @@ public function getUserObject(): ?array ->select('*') ->from('fe_users') ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($this->user, \PDO::PARAM_INT))) - ->execute() + ->executeQuery() ->fetch(); } diff --git a/Classes/Domain/Repository/LogRepository.php b/Classes/Domain/Repository/LogRepository.php index 3a9d793..9cdd92e 100644 --- a/Classes/Domain/Repository/LogRepository.php +++ b/Classes/Domain/Repository/LogRepository.php @@ -195,6 +195,6 @@ public function logDownload(AbstractToken $token, int $fileSize, string $mimeTyp } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_securedownloads_domain_model_log'); - $queryBuilder->insert('tx_securedownloads_domain_model_log')->values($log->toArray())->execute(); + $queryBuilder->insert('tx_securedownloads_domain_model_log')->values($log->toArray())->executeStatement(); } } diff --git a/Classes/Domain/Repository/StorageRepository.php b/Classes/Domain/Repository/StorageRepository.php index a3dd40f..1b327b7 100644 --- a/Classes/Domain/Repository/StorageRepository.php +++ b/Classes/Domain/Repository/StorageRepository.php @@ -72,7 +72,7 @@ public function createLocalStorage($name, $basePath, $pathType, $description = ' ->set('is_public', 0) ->set('driver', SecureDownloadsDriver::DRIVER_SHORT_NAME) ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($storageId, \PDO::PARAM_INT))) - ->execute(); + ->executeStatement(); return $storageId; } @@ -92,7 +92,7 @@ public function findByStorageType($storageType): array ->select('*') ->from($this->table) ->where($queryBuilder->expr()->eq('driver', $queryBuilder->createNamedParameter(SecureDownloadsDriver::DRIVER_SHORT_NAME))) - ->execute() + ->executeQuery() ->fetchAll(); } diff --git a/Classes/EventListener/SecureDownloadsEventListener.php b/Classes/EventListener/SecureDownloadsEventListener.php index 3c13eb2..29eda46 100644 --- a/Classes/EventListener/SecureDownloadsEventListener.php +++ b/Classes/EventListener/SecureDownloadsEventListener.php @@ -16,7 +16,6 @@ use Leuchtfeuer\SecureDownloads\Resource\Driver\SecureDownloadsDriver; use Leuchtfeuer\SecureDownloads\Service\SecureDownloadService; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Imaging\Event\ModifyIconForResourcePropertiesEvent; use TYPO3\CMS\Core\Resource\Driver\AbstractHierarchicalFilesystemDriver; use TYPO3\CMS\Core\Resource\Event\GeneratePublicUrlForResourceEvent; @@ -25,7 +24,6 @@ use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\ProcessedFile; use TYPO3\CMS\Core\SingletonInterface; -use TYPO3\CMS\Core\Utility\PathUtility; /** * This event listener listens to PSR-14 events given in TYPO3 10 and above. @@ -35,7 +33,7 @@ class SecureDownloadsEventListener implements SingletonInterface /** * @var SecureDownloadService */ - protected $secureDownloadService; + protected SecureDownloadService $secureDownloadService; public function __construct(SecureDownloadService $secureDownloadService) { @@ -62,7 +60,7 @@ public function onResourceStorageEmitPreGeneratePublicUrlSignal(GeneratePublicUr } $publicUrl = $driver->getPublicUrl($resource->getIdentifier()) ?? ''; if ($originalPathShouldBeSecured || $driver instanceof SecureDownloadsDriver || $this->secureDownloadService->pathShouldBeSecured($publicUrl)) { - $securedUrl = $this->getSecuredUrl($event->isRelativeToCurrentScript(), $publicUrl, $driver); + $securedUrl = $this->getSecuredUrl($publicUrl); $event->setPublicUrl($securedUrl); } } catch (Exception $exception) { @@ -104,27 +102,11 @@ public function onIconFactoryEmitBuildIconForResourceSignal(ModifyIconForResourc /** * Returns the encrypted URL. * - * @param bool $relativeToCurrentScript Whether the $publicUrl is relative to current script - * or not. - * @param string $publicUrl The public URL to the file. - * @param AbstractHierarchicalFilesystemDriver $driver The driver which is responsible for the file. - * + * @param string $publicUrl The public URL to the file. * @return string The secured URL */ - protected function getSecuredUrl(bool $relativeToCurrentScript, string $publicUrl, AbstractHierarchicalFilesystemDriver $driver): string + protected function getSecuredUrl(string $publicUrl): string { - if ($relativeToCurrentScript === true) { - $absolutePathToContainingFolder = PathUtility::dirname( - sprintf( - '%s/%s', - Environment::getPublicPath(), - $driver->getDefaultFolder() - ) - ); - - $pathPart = PathUtility::getRelativePathTo($absolutePathToContainingFolder); - } - - return ($pathPart ?? '') . $this->secureDownloadService->getResourceUrl($publicUrl); + return $this->secureDownloadService->getResourceUrl($publicUrl); } } diff --git a/Classes/Factory/SecureLinkFactory.php b/Classes/Factory/SecureLinkFactory.php index 869e1fc..5311b2a 100644 --- a/Classes/Factory/SecureLinkFactory.php +++ b/Classes/Factory/SecureLinkFactory.php @@ -22,8 +22,11 @@ use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\UserAspect; use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; +use TYPO3\CMS\Core\Http\ApplicationType; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; class SecureLinkFactory implements SingletonInterface @@ -33,44 +36,61 @@ class SecureLinkFactory implements SingletonInterface /** * @var EventDispatcher */ - private $eventDispatcher; + private EventDispatcher $eventDispatcher; /** * @var ExtensionConfiguration */ - private $extensionConfiguration; + private ExtensionConfiguration $extensionConfiguration; /** * @var AbstractToken */ - private $token; + private AbstractToken $token; + /** + * @throws ContentRenderingException + */ public function __construct(EventDispatcher $eventDispatcher, ExtensionConfiguration $extensionConfiguration) { $this->eventDispatcher = $eventDispatcher; $this->extensionConfiguration = $extensionConfiguration; $this->token = TokenRegistry::getToken(); - $this->init(); + $this->initializeToken(); } /** * Initialize the token. + * @throws ContentRenderingException */ - protected function init() + protected function initializeToken(): void { $this->token->setExp($this->calculateLinkLifetime()); - $this->token->setPage((int)($GLOBALS['TSFE']->id ?? 0)); + $request = $this->getRequest(); + if (ApplicationType::fromRequest($request)->isFrontend()) { + $pageArguments = $request->getAttribute('routing'); + $pageId = $pageArguments->getPageId(); + } elseif (ApplicationType::fromRequest($request)->isBackend()) { + $site = $request->getAttribute('site'); + $pageId = $site->getRootPageId(); + } + $this->token->setPage($pageId ?? 0); try { /** @var UserAspect $userAspect */ $userAspect = GeneralUtility::makeInstance(Context::class)->getAspect('frontend.user'); $this->token->setUser($userAspect->get('id')); $this->token->setGroups($userAspect->getGroupIds()); - } catch (\Exception $exception) { + } catch (\Exception) { // Do nothing. } } + private function getRequest(): ServerRequest + { + return $GLOBALS['TYPO3_REQUEST']; + } + /** * Adds the configured additional cache time and the cache lifetime of the current site to the actual time. * diff --git a/Classes/Middleware/FileDeliveryMiddleware.php b/Classes/Middleware/FileDeliveryMiddleware.php index c31c73c..2c81fac 100644 --- a/Classes/Middleware/FileDeliveryMiddleware.php +++ b/Classes/Middleware/FileDeliveryMiddleware.php @@ -48,7 +48,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { if ($this->isResponsible($request)) { $frontendUserAuthentication = $request->getAttribute('frontend.user'); - $frontendUserAuthentication->fetchGroupData(); + $frontendUserAuthentication->fetchGroupData($request); $cleanPath = mb_substr(urldecode($request->getUri()->getPath()), mb_strlen($this->assetPrefix)); [$jwt, $basePath] = explode('/', $cleanPath); diff --git a/Classes/Middleware/TokenRefreshMiddleware.php b/Classes/Middleware/TokenRefreshMiddleware.php index adbb0f5..5a6e54b 100644 --- a/Classes/Middleware/TokenRefreshMiddleware.php +++ b/Classes/Middleware/TokenRefreshMiddleware.php @@ -86,10 +86,11 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if (preg_match_all($pattern, $content, $foundJwtTokens)) { foreach ($foundJwtTokens[1] as $foundJwtToken) { try { - $data = JWT::decode($foundJwtToken, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], ['HS256']); + $headers = (object) ['alg' => 'HS256']; + $data = JWT::decode($foundJwtToken, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $headers); if ((int)$data->user !== $currentUserId) { $data->user = $currentUserId; - $newToken = JWT::encode($data, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256'); + $newToken = JWT::encode((array)$data, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256'); $replaces[$foundJwtToken] = $newToken; } } catch (\Exception $exception) { diff --git a/Classes/Resource/FileDelivery.php b/Classes/Resource/FileDelivery.php index 8a256a9..2e0dbac 100644 --- a/Classes/Resource/FileDelivery.php +++ b/Classes/Resource/FileDelivery.php @@ -45,22 +45,22 @@ class FileDelivery implements SingletonInterface /** * @var ExtensionConfiguration */ - protected $extensionConfiguration; + protected ExtensionConfiguration $extensionConfiguration; /** * @var EventDispatcherInterface */ - protected $eventDispatcher; + protected EventDispatcherInterface $eventDispatcher; /** * @var AbstractToken */ - protected $token; + protected AbstractToken $token; /** * @var array */ - protected $header = []; + protected array $header = []; public function __construct(ExtensionConfiguration $extensionConfiguration, EventDispatcher $eventDispatcher) { @@ -202,14 +202,14 @@ protected function isBackendUser(): bool * * @return StreamInterface|string Whether a stream or a string, when x-accel-redirect is used */ - protected function getResponseBody(string $file, string $fileName) + protected function getResponseBody(string $file, string $fileName): StreamInterface|string { $fileExtension = pathinfo($file, PATHINFO_EXTENSION); $forceDownload = $this->shouldForceDownload($fileExtension); $fileSize = filesize($file); $mimeType = (new FileInfo($file))->getMimeType() ?? MimeTypes::DEFAULT_MIME_TYPE; $outputFunction = $this->extensionConfiguration->getOutputFunction(); - $header = $this->getHeader($mimeType, $fileName, $forceDownload, $fileSize); + $header = $this->getFileHeader($mimeType, $fileName, $forceDownload, $fileSize); $this->dispatchBeforeFileDeliverEvent($outputFunction, $header, $fileName, $mimeType, $forceDownload); $this->header = $header; @@ -258,7 +258,7 @@ protected function shouldForceDownload(string $fileExtension): bool * * @return string[] An array of HTTP header */ - protected function getHeader(string $mimeType, string $fileName, bool $forceDownload, int $fileSize): array + protected function getFileHeader(string $mimeType, string $fileName, bool $forceDownload, int $fileSize): array { $header = [ 'Pragma' => 'private', diff --git a/Classes/UserFunctions/CheckConfiguration.php b/Classes/UserFunctions/CheckConfiguration.php index 0d51960..2c3ffc0 100644 --- a/Classes/UserFunctions/CheckConfiguration.php +++ b/Classes/UserFunctions/CheckConfiguration.php @@ -26,47 +26,47 @@ class CheckConfiguration implements SingletonInterface /** * @var ExtensionConfiguration */ - protected $extensionConfiguration; + protected mixed $extensionConfiguration; /** * @var string */ - protected $directoryPattern = ''; + protected string $directoryPattern = ''; /** * @var string */ - protected $fileTypePattern = ''; + protected string $fileTypePattern = ''; /** * @var string */ - protected $domain = ''; + protected string $domain = ''; /** * @var int */ - protected $fileCount = 0; + protected int $fileCount = 0; /** * @var array */ - protected $directories = []; + protected array $directories = []; /** * @var array */ - protected $unprotectedDirectories = []; + protected array $unprotectedDirectories = []; /** * @var array */ - protected $protectedDirectories = []; + protected array $protectedDirectories = []; /** * @var array */ - protected $unprotectedFiles = []; + protected array $unprotectedFiles = []; /** * @param ExtensionConfiguration|null $extensionConfiguration @@ -98,7 +98,7 @@ public function renderCheckAccess(): string } // .htaccess check is only available for Apache web server - if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === 0) { + if (isset($_SERVER['SERVER_SOFTWARE']) && str_starts_with($_SERVER['SERVER_SOFTWARE'], 'Apache')) { $this->checkDirectories(); if (!empty($this->unprotectedDirectories)) { @@ -130,7 +130,7 @@ protected function isDirectoryMatching(string $directoryPath): bool { $result = preg_match($this->directoryPattern, $directoryPath) === 1; - if (!$result && substr($directoryPath, 0, 1) === '/') { + if (!$result && str_starts_with($directoryPath, '/')) { return $this->isDirectoryMatching(substr($directoryPath, 1)); } @@ -148,10 +148,10 @@ protected function setDirectories(): void } /** - * @param Finder $directories + * @param Finder $directories * @param string $publicDirectory */ - protected function getSuitableDirectories(Finder $directories, string $publicDirectory) + protected function getSuitableDirectories(Finder $directories, string $publicDirectory): void { foreach ($directories as $directory) { $directoryPath = sprintf('%s/%s', $publicDirectory, $directory->getRelativePathname()); @@ -211,7 +211,7 @@ protected function checkDirectories(): void $lastSecuredDirectory = null; foreach ($this->directories as $directory) { - if ($lastSecuredDirectory && strpos($directory, $lastSecuredDirectory) === 0) { + if ($lastSecuredDirectory && str_starts_with($directory, $lastSecuredDirectory)) { continue; } @@ -316,7 +316,7 @@ protected function getFileErrorContent(): string $content .= '

Only the first ten results are shown.

'; } - if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === 0) { + if (isset($_SERVER['SERVER_SOFTWARE']) && str_starts_with($_SERVER['SERVER_SOFTWARE'], 'Apache')) { $content .= '

Here is some example code which can be used depending on your Apache version:

'; $content .= $this->getHtaccessExamples(); } diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 85591b7..cd26aaa 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -17,3 +17,6 @@ services: identifier: sdl.onIconFactoryEmitBuilderIconForResource method: onIconFactoryEmitBuildIconForResourceSignal event: TYPO3\CMS\Core\Imaging\Event\ModifyIconForResourcePropertiesEvent + + Leuchtfeuer\SecureDownloads\Controller\LogController: + tags: ['backend.controller'] \ No newline at end of file diff --git a/Configuration/TCA/tx_securedownloads_domain_model_log.php b/Configuration/TCA/tx_securedownloads_domain_model_log.php index ffb1d18..b73bb98 100644 --- a/Configuration/TCA/tx_securedownloads_domain_model_log.php +++ b/Configuration/TCA/tx_securedownloads_domain_model_log.php @@ -7,7 +7,6 @@ 'label' => 'file_id', 'tstamp' => 'tstamp', 'crdate' => 'crdate', - 'cruser_id' => 'cruser_id', 'versioningWS' => false, 'enablecolumns' => [], 'hideTable' => true, diff --git a/Libraries/vendor/composer/InstalledVersions.php b/Libraries/vendor/composer/InstalledVersions.php index c6b54af..3d1dea5 100644 --- a/Libraries/vendor/composer/InstalledVersions.php +++ b/Libraries/vendor/composer/InstalledVersions.php @@ -318,7 +318,7 @@ public static function reload($data) private static function getInstalled() { if (null === self::$canGetVendors) { - self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + self::$canGetVendors = method_exists(ClassLoader::class, 'getRegisteredLoaders'); } $installed = array(); diff --git a/Libraries/vendor/composer/autoload_classmap.php b/Libraries/vendor/composer/autoload_classmap.php index 6d471fc..b76e3fb 100644 --- a/Libraries/vendor/composer/autoload_classmap.php +++ b/Libraries/vendor/composer/autoload_classmap.php @@ -1,17 +1,25 @@ $vendorDir . '/composer/InstalledVersions.php', - 'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php', - 'Firebase\\JWT\\CachedKeySet' => $vendorDir . '/firebase/php-jwt/src/CachedKeySet.php', - 'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php', - 'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php', - 'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php', - 'Firebase\\JWT\\Key' => $vendorDir . '/firebase/php-jwt/src/Key.php', - 'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php', + InstalledVersions::class => $vendorDir . '/composer/InstalledVersions.php', + BeforeValidException::class => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php', + CachedKeySet::class => $vendorDir . '/firebase/php-jwt/src/CachedKeySet.php', + ExpiredException::class => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php', + JWK::class => $vendorDir . '/firebase/php-jwt/src/JWK.php', + JWT::class => $vendorDir . '/firebase/php-jwt/src/JWT.php', + Key::class => $vendorDir . '/firebase/php-jwt/src/Key.php', + SignatureInvalidException::class => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php', ); diff --git a/Libraries/vendor/composer/autoload_real.php b/Libraries/vendor/composer/autoload_real.php index 764648d..f44da6b 100644 --- a/Libraries/vendor/composer/autoload_real.php +++ b/Libraries/vendor/composer/autoload_real.php @@ -1,20 +1,21 @@ setClassMapAuthoritative(true); $loader->register(true); diff --git a/Libraries/vendor/composer/autoload_static.php b/Libraries/vendor/composer/autoload_static.php index c39ff4b..b2108fc 100644 --- a/Libraries/vendor/composer/autoload_static.php +++ b/Libraries/vendor/composer/autoload_static.php @@ -4,6 +4,14 @@ namespace Composer\Autoload; +use Composer\InstalledVersions; +use Firebase\JWT\BeforeValidException; +use Firebase\JWT\CachedKeySet; +use Firebase\JWT\ExpiredException; +use Firebase\JWT\JWK; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use Firebase\JWT\SignatureInvalidException; class ComposerStaticInit64b631d7d31383d9b1d9c3bbb5bb21d0 { public static $prefixLengthsPsr4 = array ( @@ -21,14 +29,14 @@ class ComposerStaticInit64b631d7d31383d9b1d9c3bbb5bb21d0 ); public static $classMap = array ( - 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php', - 'Firebase\\JWT\\CachedKeySet' => __DIR__ . '/..' . '/firebase/php-jwt/src/CachedKeySet.php', - 'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php', - 'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php', - 'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php', - 'Firebase\\JWT\\Key' => __DIR__ . '/..' . '/firebase/php-jwt/src/Key.php', - 'Firebase\\JWT\\SignatureInvalidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php', + InstalledVersions::class => __DIR__ . '/..' . '/composer/InstalledVersions.php', + BeforeValidException::class => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php', + CachedKeySet::class => __DIR__ . '/..' . '/firebase/php-jwt/src/CachedKeySet.php', + ExpiredException::class => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php', + JWK::class => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php', + JWT::class => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php', + Key::class => __DIR__ . '/..' . '/firebase/php-jwt/src/Key.php', + SignatureInvalidException::class => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/Migrations/Code/ClassAliasMap.php b/Migrations/Code/ClassAliasMap.php index 875471a..0c4d2d7 100644 --- a/Migrations/Code/ClassAliasMap.php +++ b/Migrations/Code/ClassAliasMap.php @@ -1,22 +1,22 @@ \Leuchtfeuer\SecureDownloads\Cache\AbstractCache::class, - '\Bitmotion\SecureDownloads\Cache\DecodeCache' => \Leuchtfeuer\SecureDownloads\Cache\DecodeCache::class, - '\Bitmotion\SecureDownloads\Cache\EncodeCache' => \Leuchtfeuer\SecureDownloads\Cache\EncodeCache::class, - '\Bitmotion\SecureDownloads\Controller\LogController' => \Leuchtfeuer\SecureDownloads\Controller\LogController::class, - '\Bitmotion\SecureDownloads\Domain\Model\Log' => \Leuchtfeuer\SecureDownloads\Domain\Model\Log::class, - '\Bitmotion\SecureDownloads\Domain\Repository\LogRepository' => \Leuchtfeuer\SecureDownloads\Domain\Repository\LogRepository::class, - '\Bitmotion\SecureDownloads\Domain\Transfer\ExtensionConfiguration' => \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration::class, - '\Bitmotion\SecureDownloads\Domain\Transfer\Filter' => \Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter::class, - '\Bitmotion\SecureDownloads\Domain\Transfer\Statistic' => \Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic::class, - '\Bitmotion\SecureDownloads\EventListener\SecureDownloadsEventListener' => \Leuchtfeuer\SecureDownloads\EventListener\SecureDownloadsEventListener::class, - '\Bitmotion\SecureDownloads\Factory\SecureLinkFactory' => \Leuchtfeuer\SecureDownloads\Factory\SecureLinkFactory::class, - '\Bitmotion\SecureDownloads\Middleware\FileDeliveryMiddleware' => \Leuchtfeuer\SecureDownloads\Middleware\FileDeliveryMiddleware::class, - '\Bitmotion\SecureDownloads\Resource\FileDelivery' => \Leuchtfeuer\SecureDownloads\Resource\FileDelivery::class, + '\\' . \Bitmotion\SecureDownloads\Cache\AbstractCache::class => \Leuchtfeuer\SecureDownloads\Cache\AbstractCache::class, + '\\' . \Bitmotion\SecureDownloads\Cache\DecodeCache::class => \Leuchtfeuer\SecureDownloads\Cache\DecodeCache::class, + '\\' . \Bitmotion\SecureDownloads\Cache\EncodeCache::class => \Leuchtfeuer\SecureDownloads\Cache\EncodeCache::class, + '\\' . \Bitmotion\SecureDownloads\Controller\LogController::class => \Leuchtfeuer\SecureDownloads\Controller\LogController::class, + '\\' . \Bitmotion\SecureDownloads\Domain\Model\Log::class => \Leuchtfeuer\SecureDownloads\Domain\Model\Log::class, + '\\' . \Bitmotion\SecureDownloads\Domain\Repository\LogRepository::class => \Leuchtfeuer\SecureDownloads\Domain\Repository\LogRepository::class, + '\\' . \Bitmotion\SecureDownloads\Domain\Transfer\ExtensionConfiguration::class => \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration::class, + '\\' . \Bitmotion\SecureDownloads\Domain\Transfer\Filter::class => \Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter::class, + '\\' . \Bitmotion\SecureDownloads\Domain\Transfer\Statistic::class => \Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic::class, + '\\' . \Bitmotion\SecureDownloads\EventListener\SecureDownloadsEventListener::class => \Leuchtfeuer\SecureDownloads\EventListener\SecureDownloadsEventListener::class, + '\\' . \Bitmotion\SecureDownloads\Factory\SecureLinkFactory::class => \Leuchtfeuer\SecureDownloads\Factory\SecureLinkFactory::class, + '\\' . \Bitmotion\SecureDownloads\Middleware\FileDeliveryMiddleware::class => \Leuchtfeuer\SecureDownloads\Middleware\FileDeliveryMiddleware::class, + '\\' . \Bitmotion\SecureDownloads\Resource\FileDelivery::class => \Leuchtfeuer\SecureDownloads\Resource\FileDelivery::class, '\Bitmotion\SecureDownloads\Resource\Event\AfterFileRetrievedEvent' => \Leuchtfeuer\SecureDownloads\Resource\Event\AfterFileRetrievedEvent::class, '\Bitmotion\SecureDownloads\Resource\Event\BeforeReadDeliverEvent' => \Leuchtfeuer\SecureDownloads\Resource\Event\BeforeReadDeliverEvent::class, '\Bitmotion\SecureDownloads\Resource\Event\OutputInitializationEvent' => \Leuchtfeuer\SecureDownloads\Resource\Event\OutputInitializationEvent::class, - '\Bitmotion\SecureDownloads\Service\SecureDownloadService' => \Leuchtfeuer\SecureDownloads\Service\SecureDownloadService::class, - '\Bitmotion\SecureDownloads\UserFunctions\CheckConfiguration' => \Leuchtfeuer\SecureDownloads\UserFunctions\CheckConfiguration::class, + '\\' . \Bitmotion\SecureDownloads\Service\SecureDownloadService::class => \Leuchtfeuer\SecureDownloads\Service\SecureDownloadService::class, + '\\' . \Bitmotion\SecureDownloads\UserFunctions\CheckConfiguration::class => \Leuchtfeuer\SecureDownloads\UserFunctions\CheckConfiguration::class, ]; diff --git a/composer.json b/composer.json index 25bb4b5..e6dc317 100644 --- a/composer.json +++ b/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": "^7.2 || ^8.0", - "typo3/cms-backend": "^10.4 || ^11.5", - "typo3/cms-core": "^10.4 || ^11.5", - "typo3/cms-extbase": "^10.4 || ^11.5", - "typo3/cms-frontend": "^10.4 || ^11.5", + "php": "^8.1", + "typo3/cms-backend": "^12.4", + "typo3/cms-core": "^12.4", + "typo3/cms-extbase": "^12.4", + "typo3/cms-frontend": "^12.4", "firebase/php-jwt": "^6.1", "ext-pdo": "*" }, diff --git a/ext_emconf.php b/ext_emconf.php index 833e73d..e317cf2 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -4,7 +4,7 @@ 'title' => 'Secure Downloads', 'description' => '"Secure Download": Apply TYPO3 access rights to ALL file assets (PDFs, TGZs or JPGs etc. - configurable) - protect them from direct access.', 'category' => 'fe', - 'version' => '5.0.1', + 'version' => '6.0.0', 'state' => 'stable', 'clearCacheOnLoad' => true, 'author' => 'Dev Leuchtfeuer', @@ -12,8 +12,8 @@ 'author_company' => 'Leuchtfeuer Digital Marketing', 'constraints' => [ 'depends' => [ - 'php' => '7.2.0-8.1.99', - 'typo3' => '10.4.0-11.9.99', + 'php' => '8.1.0-8.1.99', + 'typo3' => '12.4.0-12.9.99', ], 'conflicts' => [ 'naw_securedl' => '', diff --git a/ext_localconf.php b/ext_localconf.php index 846f229..6b35c4c 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,5 +1,5 @@ Date: Tue, 20 Jun 2023 12:35:51 +0200 Subject: [PATCH 02/15] [TASK][WIP] Refactor code for PHP >=7.4. --- Classes/Domain/Model/Log.php | 6 +++++- Classes/Domain/Repository/LogRepository.php | 12 +++++++----- Classes/Domain/Repository/StorageRepository.php | 8 +++++--- .../EventListener/SecureDownloadsEventListener.php | 13 +------------ Classes/Middleware/TokenRefreshMiddleware.php | 2 +- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Classes/Domain/Model/Log.php b/Classes/Domain/Model/Log.php index 95dda70..878a2b8 100644 --- a/Classes/Domain/Model/Log.php +++ b/Classes/Domain/Model/Log.php @@ -14,6 +14,7 @@ * ***/ +use Doctrine\DBAL\Exception; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; @@ -75,6 +76,9 @@ public function setTstamp(int $tstamp): void $this->tstamp = $tstamp; } + /** + * @throws Exception + */ public function getUserObject(): ?array { if ($this->user !== null && $this->user !== 0) { @@ -85,7 +89,7 @@ public function getUserObject(): ?array ->from('fe_users') ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($this->user, \PDO::PARAM_INT))) ->executeQuery() - ->fetch(); + ->fetchAssociative(); } return null; diff --git a/Classes/Domain/Repository/LogRepository.php b/Classes/Domain/Repository/LogRepository.php index 9cdd92e..1f98230 100644 --- a/Classes/Domain/Repository/LogRepository.php +++ b/Classes/Domain/Repository/LogRepository.php @@ -18,6 +18,7 @@ use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Token\AbstractToken; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException; @@ -94,7 +95,7 @@ protected function applyFilter(QueryInterface &$query, Filter $filter): void $this->applyEqualPropertyToFilter((int)$filter->getPageId(), 'page', $query, $constraints); if (count($constraints) > 0) { - $query->matching($query->logicalAnd($constraints)); + $query->matching($query->logicalAnd(...$constraints)); } } @@ -170,10 +171,11 @@ protected function applyEqualPropertyToFilter(int $property, string $propertyNam /** * Creates a log entry in the database. * - * @param AbstractToken $token The token containing information that should be logged - * @param int $fileSize The file size of the file that should be logged - * @param string $mimeType The mime type of the file that should be logged - * @param int $user The ID of the user that downloaded the file + * @param AbstractToken $token The token containing information that should be logged + * @param int $fileSize The file size of the file that should be logged + * @param string $mimeType The mime type of the file that should be logged + * @param int $user The ID of the user that downloaded the file + * @throws ResourceDoesNotExistException */ public function logDownload(AbstractToken $token, int $fileSize, string $mimeType, int $user): void { diff --git a/Classes/Domain/Repository/StorageRepository.php b/Classes/Domain/Repository/StorageRepository.php index 1b327b7..e975fc6 100644 --- a/Classes/Domain/Repository/StorageRepository.php +++ b/Classes/Domain/Repository/StorageRepository.php @@ -14,6 +14,7 @@ * ***/ +use Doctrine\DBAL\Exception; use Leuchtfeuer\SecureDownloads\Resource\Driver\SecureDownloadsDriver; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -26,7 +27,7 @@ class StorageRepository extends \TYPO3\CMS\Core\Resource\StorageRepository * Creates the "Secure Downloads" file storage object if not exists and if the extension configuration option is enabled. This * method will also create the directory containing the assets and puts an .htaccess file into that directory. */ - public function createSecureDownloadStorage() + public function createSecureDownloadStorage(): void { $path = sprintf('%s/%s', Environment::getPublicPath(), SecureDownloadsDriver::BASE_PATH); @@ -62,7 +63,7 @@ public function createSecureDownloadStorage() * * @return int id of the inserted record */ - public function createLocalStorage($name, $basePath, $pathType, $description = '', $default = false) + public function createLocalStorage($name, $basePath, $pathType, $description = '', $default = false): int { $storageId = parent::createLocalStorage($name, $basePath, $pathType, $description, $default); @@ -83,6 +84,7 @@ public function createLocalStorage($name, $basePath, $pathType, $description = ' * @param string $storageType The identifier of the storage. * * @return ResourceStorage[] + * @throws Exception */ public function findByStorageType($storageType): array { @@ -93,7 +95,7 @@ public function findByStorageType($storageType): array ->from($this->table) ->where($queryBuilder->expr()->eq('driver', $queryBuilder->createNamedParameter(SecureDownloadsDriver::DRIVER_SHORT_NAME))) ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** diff --git a/Classes/EventListener/SecureDownloadsEventListener.php b/Classes/EventListener/SecureDownloadsEventListener.php index 29eda46..7eaff8e 100644 --- a/Classes/EventListener/SecureDownloadsEventListener.php +++ b/Classes/EventListener/SecureDownloadsEventListener.php @@ -60,7 +60,7 @@ public function onResourceStorageEmitPreGeneratePublicUrlSignal(GeneratePublicUr } $publicUrl = $driver->getPublicUrl($resource->getIdentifier()) ?? ''; if ($originalPathShouldBeSecured || $driver instanceof SecureDownloadsDriver || $this->secureDownloadService->pathShouldBeSecured($publicUrl)) { - $securedUrl = $this->getSecuredUrl($publicUrl); + $securedUrl = $this->secureDownloadService->getResourceUrl($publicUrl); $event->setPublicUrl($securedUrl); } } catch (Exception $exception) { @@ -98,15 +98,4 @@ public function onIconFactoryEmitBuildIconForResourceSignal(ModifyIconForResourc $event->setOverlayIdentifier($overlayIdentifier ?? $event->getOverlayIdentifier()); } - - /** - * Returns the encrypted URL. - * - * @param string $publicUrl The public URL to the file. - * @return string The secured URL - */ - protected function getSecuredUrl(string $publicUrl): string - { - return $this->secureDownloadService->getResourceUrl($publicUrl); - } } diff --git a/Classes/Middleware/TokenRefreshMiddleware.php b/Classes/Middleware/TokenRefreshMiddleware.php index 5a6e54b..841dbf3 100644 --- a/Classes/Middleware/TokenRefreshMiddleware.php +++ b/Classes/Middleware/TokenRefreshMiddleware.php @@ -86,7 +86,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if (preg_match_all($pattern, $content, $foundJwtTokens)) { foreach ($foundJwtTokens[1] as $foundJwtToken) { try { - $headers = (object) ['alg' => 'HS256']; + $headers = (object)['alg' => 'HS256']; $data = JWT::decode($foundJwtToken, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $headers); if ((int)$data->user !== $currentUserId) { $data->user = $currentUserId; From f5ebf850e3217fbe760fce0b0086d02b82384ac6 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Tue, 20 Jun 2023 14:25:09 +0200 Subject: [PATCH 03/15] [TASK][WIP] Refactor Traffic Log backend module and its templates. - Use be_user module data instead of PHP Session to store filter settings. - Remove 'showAction', as this was almost the same as 'listAction'. Therefore, traffic data per page can be viewed by select a page, all traffic can be viewed per click on TYPO3 root page UID 0. - Removed/replaced deprecated functions. - Icon factory added. - Module registration moved. --- Classes/Controller/LogController.php | 179 +++++------------- Configuration/Backend/Modules.php | 20 ++ Configuration/Icons.php | 23 +++ Resources/Private/Layouts/Default.html | 39 ++-- .../Private/Partials/Backend/Filter.html | 111 ++++++----- Resources/Private/Partials/Backend/View.html | 25 ++- Resources/Private/Templates/Log/List.html | 46 ++++- Resources/Private/Templates/Log/Show.html | 7 - ext_tables.php | 17 -- 9 files changed, 224 insertions(+), 243 deletions(-) create mode 100644 Configuration/Backend/Modules.php create mode 100644 Configuration/Icons.php delete mode 100644 Resources/Private/Templates/Log/Show.html diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 99906d5..5a7404a 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -19,23 +19,18 @@ use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Backend\Template\Components\Menu\Menu; use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Pagination\ArrayPaginator; use TYPO3\CMS\Core\Pagination\SimplePagination; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; -use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; class LogController extends ActionController { - const FILTER_SESSION_KEY = 'sdl-filter'; - /** * @var LogRepository $logRepository */ @@ -46,31 +41,19 @@ class LogController extends ActionController */ protected ModuleTemplateFactory $moduleTemplateFactory; - public function __construct(LogRepository $logRepository, ModuleTemplateFactory $moduleTemplateFactory) - { - $this->logRepository = $logRepository; + public function __construct( + ModuleTemplateFactory $moduleTemplateFactory, + LogRepository $logRepository, + ) { $this->moduleTemplateFactory = $moduleTemplateFactory; + $this->logRepository = $logRepository; } /** * @return ResponseInterface - * @throws NoSuchArgumentException */ public function initializeAction(): ResponseInterface { - parent::initializeAction(); - - if ($this->arguments->hasArgument('filter')) { - $this->arguments->getArgument('filter')->getPropertyMappingConfiguration()->allowAllProperties(); - } - - if ($this->request->hasArgument('reset') && (bool)$this->request->getArgument('reset') === true) { - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter())); - } - - if ($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY) === null) { - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter())); - } $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); return $this->htmlResponse(''); @@ -83,21 +66,29 @@ public function initializeAction(): ResponseInterface */ public function listAction(?Filter $filter = null): ResponseInterface { - $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); - $filter->setPageId(0); + if ($this->request->hasArgument('reset') && (bool)$this->request->getArgument('reset') === true) { + $filter = new Filter(); + } elseif ($filter === null) { + $filter = $this->getFilterFromBeUserData(); + } + + $pageId = $this->request->getQueryParams()['id'] ?? 0; + $filter->setPageId((int)$pageId); $logEntries = $this->logRepository->findByFilter($filter); - // Store filter data in session of backend user (used for pagination) - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter)); + $this->persistFilterInBeUserData($filter); + $this->resetFilterOnMemoryExhaustionError(); $itemsPerPage = 20; - $currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1; + $currentPage = array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; - $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); + $paginator = new ArrayPaginator($logEntries->toArray(), (int)$currentPage, $itemsPerPage); $pagination = new SimplePagination($paginator); - $this->view->assignMultiple([ + $moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $moduleTemplate->assignMultiple([ 'logs' => $paginator->getPaginatedItems(), + 'page' => BackendUtility::getRecord('pages', $pageId), 'users' => $this->getUsers(), 'fileTypes' => $this->getFileTypes(), 'filter' => $filter, @@ -105,11 +96,9 @@ public function listAction(?Filter $filter = null): ResponseInterface 'paginator' => $paginator, 'pagination' => $pagination, 'totalResultCount' => count($logEntries), + 'isRoot' => $pageId == 0, ]); - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $this->createMenu(); - $moduleTemplate->setContent($this->view->render()); - return $this->htmlResponse($moduleTemplate->renderContent()); + return $moduleTemplate->renderResponse('List'); } /** @@ -127,7 +116,7 @@ private function getUsers(): array ->where($queryBuilder->expr()->neq('user', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))) ->groupBy('users.uid') ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** @@ -143,116 +132,52 @@ private function getFileTypes(): array ->from('tx_securedownloads_domain_model_log') ->groupBy('media_type')->orderBy('media_type', 'ASC') ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** - * @param Filter|null $filter The filter object - * @return ResponseInterface - * @throws Exception + * Get module states (the filter object) from user data */ - public function showAction(?Filter $filter = null): ResponseInterface + protected function getFilterFromBeUserData(): Filter { - $pageId = (int)GeneralUtility::_GP('id'); - - if ($pageId === 0) { - return $this->redirect('list'); + $serializedConstraint = $this->request->getAttribute('moduleData')->get('filter'); + $filter = null; + if (is_string($serializedConstraint) && !empty($serializedConstraint)) { + $filter = @unserialize($serializedConstraint, ['allowed_classes' => [Filter::class, \DateTime::class]]); } - - $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); - $filter->setPageId($pageId); - $logEntries = $this->logRepository->findByFilter($filter); - - // Store filter data in session of backend user (used for pagination) - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter)); - - $itemsPerPage = 20; - $currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1; - - $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); - $pagination = new SimplePagination($paginator); - - $this->view->assignMultiple([ - 'logs' => $paginator->getPaginatedItems(), - 'page' => BackendUtility::getRecord('pages', $pageId), - 'users' => $this->getUsers(), - 'fileTypes' => $this->getFileTypes(), - 'filter' => $filter, - 'statistic' => new Statistic($logEntries), - 'paginator' => $paginator, - 'pagination' => $pagination, - 'totalResultCount' => count($logEntries), - ]); - - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $this->createMenu(); - $moduleTemplate->setContent($this->view->render()); - return $this->htmlResponse($moduleTemplate->renderContent()); + return $filter ?: GeneralUtility::makeInstance(Filter::class); } /** - * Set up the doc header properly here + * Save current filter object in be user settings (uC) */ - public function initializeView(): void + protected function persistFilterInBeUserData(Filter $filter): void { - $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); - $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); - $this->createMenu(); + $moduleData = $this->request->getAttribute('moduleData'); + $moduleData->set('filter', serialize($filter)); + $this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray()); } /** - * Create menu + * In case the script execution fails, because the user requested too many results + * (memory exhaustion in php), reset the filters in be user settings, so + * the belog can be accessed again in the next call. */ - private function createMenu(): void + protected function resetFilterOnMemoryExhaustionError(): void { - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); - $menu->setIdentifier('secure_downloads'); - - if ((int)GeneralUtility::_GP('id') !== 0) { - $this->addMenuItems($menu); - } - - $this->view->assign('action', $this->request->getControllerActionName()); - $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); - } - - /** - * Adds menu options to the select menu - * - * @param Menu $menu The Menu object - */ - protected function addMenuItems(Menu &$menu): void - { - $controllerName = $this->request->getControllerName(); - $controllerActionName = $this->request->getControllerActionName(); - $actions = [ - ['controller' => 'Log', 'action' => 'show', 'label' => 'Show by Page'], - ['controller' => 'Log', 'action' => 'list', 'label' => 'Overview'], - ]; - - foreach ($actions as $action) { - $isActive = $controllerName === $action['controller'] && $controllerActionName === $action['action']; - - $href = $this->getUriBuilder()->reset()->uriFor( - $action['action'], - [], - $action['controller'] - ); - - $item = $menu->makeMenuItem()->setTitle($action['label'])->setHref($href)->setActive($isActive); - $menu->addMenuItem($item); - } + $reservedMemory = new \SplFixedArray(187500); // 3M + register_shutdown_function(function () use (&$reservedMemory): void { + $reservedMemory = null; // free the reserved memory + $error = error_get_last(); + if (str_contains($error['message'] ?? '', 'Allowed memory size of')) { + $filter = GeneralUtility::makeInstance(Filter::class); + $this->persistFilterInBeUserData($filter); + } + }); } - /** - * @return UriBuilder The URI builder - */ - protected function getUriBuilder(): UriBuilder + protected function getBackendUser(): BackendUserAuthentication { - $uriBuilder = $this->objectManager->get(UriBuilder::class); - $uriBuilder->setRequest($this->request); - - return $uriBuilder; + return $GLOBALS['BE_USER']; } } diff --git a/Configuration/Backend/Modules.php b/Configuration/Backend/Modules.php new file mode 100644 index 0000000..8f970a3 --- /dev/null +++ b/Configuration/Backend/Modules.php @@ -0,0 +1,20 @@ + [ + 'parent' => 'web', + 'position' => ['after' => 'web_info'], + 'access' => 'user', + 'path' => '/module/page/secure-downloads', + 'labels' => 'LLL:EXT:secure_downloads/Resources/Private/Language/locallang_log.xlf', + 'extensionName' => 'Secure Downloads', + 'iconIdentifier' => 'tx_securedownloads-module', + 'controllerActions' => [ + LogController::class => [ + 'list', + ], + ], + ], +]; \ No newline at end of file diff --git a/Configuration/Icons.php b/Configuration/Icons.php new file mode 100644 index 0000000..66cd559 --- /dev/null +++ b/Configuration/Icons.php @@ -0,0 +1,23 @@ + [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:secure_downloads/Resources/Public/Icons/Extension.svg', + ], +]; diff --git a/Resources/Private/Layouts/Default.html b/Resources/Private/Layouts/Default.html index 7600649..b26fec7 100644 --- a/Resources/Private/Layouts/Default.html +++ b/Resources/Private/Layouts/Default.html @@ -1,27 +1,12 @@ - - -
- - - - -
- {statistic.traffic -> f:format.bytes()} - {statistic.from -> f:format.date(format: '{f:translate(key: \'dateformat\')}')} - {statistic.till -> f:format.date(format: '{f:translate(key: \'dateformat\')}')} - - {f:translate(key: 'module.subheading', arguments: '{0: totalResultCount, 1: traffic, 2: from, 3: till}') -> f:format.html()} -
- - -
- -
-
-
- - - - - -
+ + +
+
+ +
+ +
+
\ No newline at end of file diff --git a/Resources/Private/Partials/Backend/Filter.html b/Resources/Private/Partials/Backend/Filter.html index ec892c2..f956b3b 100644 --- a/Resources/Private/Partials/Backend/Filter.html +++ b/Resources/Private/Partials/Backend/Filter.html @@ -1,68 +1,79 @@ - + + +
-
+
- + +
-
+
-
- -
- -
-
- -
-
- -
+
+
+ + +
+
+ +
- -
-
-
- - - -
-
- -
+
+
+ + + +
+
+
- - -
diff --git a/Resources/Private/Partials/Backend/View.html b/Resources/Private/Partials/Backend/View.html index 525dc22..b2bdddf 100644 --- a/Resources/Private/Partials/Backend/View.html +++ b/Resources/Private/Partials/Backend/View.html @@ -1,16 +1,21 @@ - + + +
- - - - - - - - + + + + + + + + - +
diff --git a/Resources/Private/Templates/Log/List.html b/Resources/Private/Templates/Log/List.html index 25cb689..807c5fb 100644 --- a/Resources/Private/Templates/Log/List.html +++ b/Resources/Private/Templates/Log/List.html @@ -1,7 +1,43 @@ + + - -

- -

-
+ + + +
+

+ + + + + + [{page.uid}] + + +

+ + + + + + + + +
+
+ + + + + +
+
\ No newline at end of file diff --git a/Resources/Private/Templates/Log/Show.html b/Resources/Private/Templates/Log/Show.html deleted file mode 100644 index fb9ab7f..0000000 --- a/Resources/Private/Templates/Log/Show.html +++ /dev/null @@ -1,7 +0,0 @@ - - - -

- -

-
diff --git a/ext_tables.php b/ext_tables.php index 78f2e1b..0b9fd9c 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -3,23 +3,6 @@ call_user_func( function ($extensionKey) { - // Register the backend module if the log option is set in extension configuration - if ((new \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration())->isLog()) { - \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( - $extensionKey, - 'web', - 'TrafficLog', - '10', - [ - \Leuchtfeuer\SecureDownloads\Controller\LogController::class => 'show,list', - ], [ - 'access' => 'user,group', - 'icon' => 'EXT:secure_downloads/Resources/Public/Icons/Extension.svg', - 'labels' => 'LLL:EXT:secure_downloads/Resources/Private/Language/locallang_log.xlf', - ] - ); - } - // Create resource storage if ((new \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration())->isCreateFileStorage()) { $storageRepositoryClass = \Leuchtfeuer\SecureDownloads\Domain\Repository\StorageRepository::class; From 058708520bc22afa64fc698b115d6394c22c89ea Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Tue, 20 Jun 2023 14:29:16 +0200 Subject: [PATCH 04/15] [TASK][WIP] Remove extension configuration 'Log Module (logging.log [boolean])'. - The module will now always be active. --- ext_conf_template.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 82f1608..94a83c5 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -55,8 +55,5 @@ protectedPath = # cat=File Delivery/050; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:allowPublicAccess allowPublicAccess = 1 -# cat=Logging; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:log -log = 0 - # cat=Backend/010; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:skipCheckConfiguration skipCheckConfiguration = 0 From 0ba273a97760cd1287df7be62bfc99206a59e6e4 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Tue, 20 Jun 2023 17:36:20 +0200 Subject: [PATCH 05/15] [TASK] Undo removal of extension configuration 'log'. TER-108 TER-116 --- Classes/Controller/LogController.php | 4 ++ Resources/Private/Templates/Log/List.html | 59 ++++++++++++++--------- ext_conf_template.txt | 3 ++ 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 5a7404a..509255c 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -22,6 +22,7 @@ use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Pagination\ArrayPaginator; @@ -72,6 +73,8 @@ public function listAction(?Filter $filter = null): ResponseInterface $filter = $this->getFilterFromBeUserData(); } + $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; + $pageId = $this->request->getQueryParams()['id'] ?? 0; $filter->setPageId((int)$pageId); $logEntries = $this->logRepository->findByFilter($filter); @@ -87,6 +90,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $moduleTemplate = $this->moduleTemplateFactory->create($this->request); $moduleTemplate->assignMultiple([ + 'loggingEnabled' => $extensionConfigurationLogging, 'logs' => $paginator->getPaginatedItems(), 'page' => BackendUtility::getRecord('pages', $pageId), 'users' => $this->getUsers(), diff --git a/Resources/Private/Templates/Log/List.html b/Resources/Private/Templates/Log/List.html index 807c5fb..de8455b 100644 --- a/Resources/Private/Templates/Log/List.html +++ b/Resources/Private/Templates/Log/List.html @@ -8,31 +8,46 @@ -
-

- - - - - - [{page.uid}] - - -

+ + +
+

+ + + + + + [{page.uid}] + + +

- + + +
+ + + + {f:translate(key: 'module.subheading', arguments: '{0: totalResultCount, 1: traffic, 2: from, 3: till}') -> f:format.raw()} +
+ + + + +
+
+ +
+

Logging is disabled

+

If you want to enable logging, goto extension configuration of EXT:secure_downloads + and activate the checkbox 'Log Module' [log] + within the configuration tab 'Logging'.
Inform a system maintainer in case you + don't have access to the extension configuration.

+
+
+
- - - - -
diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 94a83c5..82f1608 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -55,5 +55,8 @@ protectedPath = # cat=File Delivery/050; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:allowPublicAccess allowPublicAccess = 1 +# cat=Logging; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:log +log = 0 + # cat=Backend/010; type=boolean; label=LLL:EXT:secure_downloads/Resources/Private/Language/locallang_em.xlf:skipCheckConfiguration skipCheckConfiguration = 0 From ca9177c52a90bc54d8cb232c7e6b519ae20d9f8a Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Wed, 21 Jun 2023 10:56:26 +0200 Subject: [PATCH 06/15] [TASK] Update testing framework. TER-108 TER-119 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e6dc317..49dbe0e 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ } }, "require-dev": { - "typo3/testing-framework": "^6.16" + "typo3/testing-framework": "^8.0.1" }, "config": { "allow-plugins": { From 20fbdbf2aeb86eacdfffc38805d49646ab92b8f0 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Wed, 21 Jun 2023 11:07:22 +0200 Subject: [PATCH 07/15] [BUGFIX] Test 'whenALinkWithAnOtherUserIDofTheCurrentUserLinkResponseBodyIsModified' fails. TER-108 TER-119 This is exactly the '[BUGFIX] JWT::decode method signature not respected #163' - Thanks to https://github.com/jpmschuler --- Classes/Middleware/TokenRefreshMiddleware.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Middleware/TokenRefreshMiddleware.php b/Classes/Middleware/TokenRefreshMiddleware.php index 841dbf3..60f3a62 100644 --- a/Classes/Middleware/TokenRefreshMiddleware.php +++ b/Classes/Middleware/TokenRefreshMiddleware.php @@ -15,6 +15,7 @@ ***/ use Firebase\JWT\JWT; +use Firebase\JWT\Key; use Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -86,8 +87,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if (preg_match_all($pattern, $content, $foundJwtTokens)) { foreach ($foundJwtTokens[1] as $foundJwtToken) { try { - $headers = (object)['alg' => 'HS256']; - $data = JWT::decode($foundJwtToken, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $headers); + $data = JWT::decode($foundJwtToken, new Key($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256')); if ((int)$data->user !== $currentUserId) { $data->user = $currentUserId; $newToken = JWT::encode((array)$data, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256'); From a2a6c984ebdaa03a721f2852e4ba84d924b55273 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Wed, 21 Jun 2023 11:25:08 +0200 Subject: [PATCH 08/15] [TASK] Move typecast to variable assignment. TER-108 TER-116 --- Classes/Controller/LogController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 509255c..56847a5 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -75,17 +75,17 @@ public function listAction(?Filter $filter = null): ResponseInterface $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; - $pageId = $this->request->getQueryParams()['id'] ?? 0; - $filter->setPageId((int)$pageId); + $pageId = (int) $this->request->getQueryParams()['id'] ?? 0; + $filter->setPageId($pageId); $logEntries = $this->logRepository->findByFilter($filter); $this->persistFilterInBeUserData($filter); $this->resetFilterOnMemoryExhaustionError(); $itemsPerPage = 20; - $currentPage = array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; + $currentPage = (int) array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; - $paginator = new ArrayPaginator($logEntries->toArray(), (int)$currentPage, $itemsPerPage); + $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); $pagination = new SimplePagination($paginator); $moduleTemplate = $this->moduleTemplateFactory->create($this->request); From cdf67f63b4769febd012527bd3e4bf504ac65c18 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Wed, 21 Jun 2023 17:28:26 +0200 Subject: [PATCH 09/15] [TASK] Update extension documentation. TER-108 TER-111 --- Documentation/About/ChangeLog/6-0-0.rst | 55 +++++++++++++++++++++++++ Documentation/About/ChangeLog/Index.rst | 1 + Documentation/About/Index.rst | 27 ++++++------ 3 files changed, 69 insertions(+), 14 deletions(-) create mode 100755 Documentation/About/ChangeLog/6-0-0.rst diff --git a/Documentation/About/ChangeLog/6-0-0.rst b/Documentation/About/ChangeLog/6-0-0.rst new file mode 100755 index 0000000..c73c3ff --- /dev/null +++ b/Documentation/About/ChangeLog/6-0-0.rst @@ -0,0 +1,55 @@ +.. include:: ../../Includes.txt + +========================== +Version 6.0.0 - 2023/06/26 +========================== + +This release is a new major release. It introduces support for TYPO3 12 LTS as well as for PHP 8.1. TYPO3 11 LTS is not supported anymore. + +Download +======== + +Download this version from the `TYPO3 extension repository `__ or from +`GitHub `__. + +Added +===== +* Support for TYPO3 version 12 + +Changed +======= +* Reworked backend module + +Deprecated +========== +* - + +Removed +======= +* Backend module action 'show' + +All Changes +=========== +This is a list of all changes in this release:: + + 2023-06-21 [TASK] Move typecast to variable assignment. (Commit a2a6c98 by Sebastian Afeldt) + 2023-06-21 [BUGFIX] Test 'whenALinkWithAnOtherUserIDofTheCurrentUserLinkResponseBodyIsModified' fails. Thanks to J. Peter M. Schuler #163 (Commit 20fbdbf by Sebastian Afeldt) + 2023-06-21 [TASK] Update testing framework. (Commit ca9177c by Sebastian Afeldt) + 2023-06-20 [TASK] Undo removal of extension configuration 'log'. (Commit 0ba273a by Sebastian Afeldt) + 2023-06-20 [TASK] Remove extension configuration 'Log Module (logging.log [boolean])'. (Commit 0587085 by Sebastian Afeldt) + 2023-06-20 [TASK] Refactor Traffic Log backend module and its templates. (Commit f5ebf85 by Sebastian Afeldt) + 2023-06-20 [TASK] Refactor code for PHP >=7.4. Thanks to Oliver Kleinecke (Commit 0d156c3 by Sebastian Afeldt) + 2023-06-16 [TASK] Support TYPO3 12.4. Thanks to sk-foresite #162 (Commit a605ecf by Sebastian Afeldt) + +Contributors +============ +Following people have contributed to this release: + +* 'sk-foresite ' +* 'J. Peter M. Schuler ' +* Oliver Kleinecke +* Marcus Balasch +* Niklas Grieger +* Sebastian Afeldt + +Thank you very much for your support. The next drink is on us! 🍻 diff --git a/Documentation/About/ChangeLog/Index.rst b/Documentation/About/ChangeLog/Index.rst index 2afc802..6d4ca65 100755 --- a/Documentation/About/ChangeLog/Index.rst +++ b/Documentation/About/ChangeLog/Index.rst @@ -17,6 +17,7 @@ List of versions :titlesonly: :glob: + 6-0-0 5-0-1 5-0-0 4-1-6 diff --git a/Documentation/About/Index.rst b/Documentation/About/Index.rst index f8141e1..607c826 100755 --- a/Documentation/About/Index.rst +++ b/Documentation/About/Index.rst @@ -32,16 +32,14 @@ As a complementary measure, you will of course need to configure your web server Compatibility ============= -We are currently supporting following TYPO3 versions:

+We are currently supporting following TYPO3 versions: -.. csv-table:: Version Matrix - :header: "Extension Version", "TYPO3 v10 Support", "TYPO3 v9 Support" +.. csv-table:: Version Matrix - Supported Versions + :header: "Extension Version", "TYPO3 v12", "TYPO3 v11", "TYPO3 v10" :align: center - "5.x", "🙋‍♂️", "🙅‍♀️" - "4.x", "🙋‍♂️", "🙋‍♂️" - -Version 5 is an upcoming release. Its package name has been changed to `leuchtfeuer/secure-downloads`. + "6.x", "yes", "no", "no" + "5.x", "no", "yes", "yes" .. _about-compatibility-outdatedVersions: @@ -49,17 +47,18 @@ Outdated Versions ----------------- For the following versions no more free bug fixes and new features will be provided by the authors: - -.. csv-table:: Version Matrix - :header: "Extension Version", "TYPO3 v9", "TYPO3 v8", "TYPO3 v7", "TYPO3 v6.2", "TYPO3 v4.5" +.. csv-table:: Version Matrix - Outdated Versions + :header: "Extension Version", "TYPO3 v10", "TYPO3 v9", "TYPO3 v8", "TYPO3 v7", "TYPO3 v6.2", "TYPO3 v4.5" :align: center - "3.x", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️", "🙅‍♀️", "🙅‍♀️" - "2.0.4 - 2.x", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️", "🙅‍♀️" - "2.0.0 - 2.0.3", "🙅‍♀️", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️" - "1.x", "🙅‍♀️", "🙅‍♀️", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️" + "4.x", "yes", "yes", "no", "no", "no", "no" + "3.x", "no", "yes", "yes", "no", "no", "no" + "2.0.4 - 2.x", "no", "no", "yes", "yes", "no", "no" + "2.0.0 - 2.0.3", "no", "no", "yes", "yes", "yes", "no" + "1.x", "no", "no", "no", "no", "no", "yes" Version 1 was released as `EXT:naw_securedl `__ or `typo3-ter/naw-securedl`. +Its package name has been changed to `leuchtfeuer/secure-downloads` since Version 5. .. _about-links: From 90ba39894939792fb3b069c492f5e1c3fa00bcad Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt <33094668+bmafeldt@users.noreply.github.com> Date: Thu, 22 Jun 2023 11:15:21 +0200 Subject: [PATCH 10/15] Update for TYPO3 12 support. TER-108 (#166) * [TASK][WIP] Refactor code for PHP >=7.4. * [TASK][WIP] Refactor Traffic Log backend module and its templates. - Use be_user module data instead of PHP Session to store filter settings. - Remove 'showAction', as this was almost the same as 'listAction'. Therefore, traffic data per page can be viewed by select a page, all traffic can be viewed per click on TYPO3 root page UID 0. - Removed/replaced deprecated functions. - Icon factory added. - Module registration moved. * [TASK][WIP] Remove extension configuration 'Log Module (logging.log [boolean])'. - The module will now always be active. * [TASK] Undo removal of extension configuration 'log'. TER-108 TER-116 * [TASK] Update testing framework. TER-108 TER-119 * [BUGFIX] Test 'whenALinkWithAnOtherUserIDofTheCurrentUserLinkResponseBodyIsModified' fails. TER-108 TER-119 This is exactly the '[BUGFIX] JWT::decode method signature not respected #163' - Thanks to https://github.com/jpmschuler * [TASK] Move typecast to variable assignment. TER-108 TER-116 * [TASK] Update extension documentation. TER-108 TER-111 --- Classes/Controller/LogController.php | 181 ++++++------------ Classes/Domain/Model/Log.php | 6 +- Classes/Domain/Repository/LogRepository.php | 12 +- .../Domain/Repository/StorageRepository.php | 8 +- .../SecureDownloadsEventListener.php | 13 +- Classes/Middleware/TokenRefreshMiddleware.php | 4 +- Configuration/Backend/Modules.php | 20 ++ Configuration/Icons.php | 23 +++ Documentation/About/ChangeLog/6-0-0.rst | 55 ++++++ Documentation/About/ChangeLog/Index.rst | 1 + Documentation/About/Index.rst | 27 ++- Resources/Private/Layouts/Default.html | 39 ++-- .../Private/Partials/Backend/Filter.html | 111 ++++++----- Resources/Private/Partials/Backend/View.html | 25 ++- Resources/Private/Templates/Log/List.html | 61 +++++- Resources/Private/Templates/Log/Show.html | 7 - composer.json | 2 +- ext_tables.php | 17 -- 18 files changed, 332 insertions(+), 280 deletions(-) create mode 100644 Configuration/Backend/Modules.php create mode 100644 Configuration/Icons.php create mode 100755 Documentation/About/ChangeLog/6-0-0.rst delete mode 100644 Resources/Private/Templates/Log/Show.html diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 99906d5..56847a5 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -19,23 +19,19 @@ use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Backend\Template\Components\Menu\Menu; use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Pagination\ArrayPaginator; use TYPO3\CMS\Core\Pagination\SimplePagination; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; -use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; class LogController extends ActionController { - const FILTER_SESSION_KEY = 'sdl-filter'; - /** * @var LogRepository $logRepository */ @@ -46,31 +42,19 @@ class LogController extends ActionController */ protected ModuleTemplateFactory $moduleTemplateFactory; - public function __construct(LogRepository $logRepository, ModuleTemplateFactory $moduleTemplateFactory) - { - $this->logRepository = $logRepository; + public function __construct( + ModuleTemplateFactory $moduleTemplateFactory, + LogRepository $logRepository, + ) { $this->moduleTemplateFactory = $moduleTemplateFactory; + $this->logRepository = $logRepository; } /** * @return ResponseInterface - * @throws NoSuchArgumentException */ public function initializeAction(): ResponseInterface { - parent::initializeAction(); - - if ($this->arguments->hasArgument('filter')) { - $this->arguments->getArgument('filter')->getPropertyMappingConfiguration()->allowAllProperties(); - } - - if ($this->request->hasArgument('reset') && (bool)$this->request->getArgument('reset') === true) { - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter())); - } - - if ($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY) === null) { - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter())); - } $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); return $this->htmlResponse(''); @@ -83,21 +67,32 @@ public function initializeAction(): ResponseInterface */ public function listAction(?Filter $filter = null): ResponseInterface { - $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); - $filter->setPageId(0); + if ($this->request->hasArgument('reset') && (bool)$this->request->getArgument('reset') === true) { + $filter = new Filter(); + } elseif ($filter === null) { + $filter = $this->getFilterFromBeUserData(); + } + + $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; + + $pageId = (int) $this->request->getQueryParams()['id'] ?? 0; + $filter->setPageId($pageId); $logEntries = $this->logRepository->findByFilter($filter); - // Store filter data in session of backend user (used for pagination) - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter)); + $this->persistFilterInBeUserData($filter); + $this->resetFilterOnMemoryExhaustionError(); $itemsPerPage = 20; - $currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1; + $currentPage = (int) array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); $pagination = new SimplePagination($paginator); - $this->view->assignMultiple([ + $moduleTemplate = $this->moduleTemplateFactory->create($this->request); + $moduleTemplate->assignMultiple([ + 'loggingEnabled' => $extensionConfigurationLogging, 'logs' => $paginator->getPaginatedItems(), + 'page' => BackendUtility::getRecord('pages', $pageId), 'users' => $this->getUsers(), 'fileTypes' => $this->getFileTypes(), 'filter' => $filter, @@ -105,11 +100,9 @@ public function listAction(?Filter $filter = null): ResponseInterface 'paginator' => $paginator, 'pagination' => $pagination, 'totalResultCount' => count($logEntries), + 'isRoot' => $pageId == 0, ]); - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $this->createMenu(); - $moduleTemplate->setContent($this->view->render()); - return $this->htmlResponse($moduleTemplate->renderContent()); + return $moduleTemplate->renderResponse('List'); } /** @@ -127,7 +120,7 @@ private function getUsers(): array ->where($queryBuilder->expr()->neq('user', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))) ->groupBy('users.uid') ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** @@ -143,116 +136,52 @@ private function getFileTypes(): array ->from('tx_securedownloads_domain_model_log') ->groupBy('media_type')->orderBy('media_type', 'ASC') ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** - * @param Filter|null $filter The filter object - * @return ResponseInterface - * @throws Exception + * Get module states (the filter object) from user data */ - public function showAction(?Filter $filter = null): ResponseInterface + protected function getFilterFromBeUserData(): Filter { - $pageId = (int)GeneralUtility::_GP('id'); - - if ($pageId === 0) { - return $this->redirect('list'); + $serializedConstraint = $this->request->getAttribute('moduleData')->get('filter'); + $filter = null; + if (is_string($serializedConstraint) && !empty($serializedConstraint)) { + $filter = @unserialize($serializedConstraint, ['allowed_classes' => [Filter::class, \DateTime::class]]); } - - $filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter()); - $filter->setPageId($pageId); - $logEntries = $this->logRepository->findByFilter($filter); - - // Store filter data in session of backend user (used for pagination) - $GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter)); - - $itemsPerPage = 20; - $currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1; - - $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); - $pagination = new SimplePagination($paginator); - - $this->view->assignMultiple([ - 'logs' => $paginator->getPaginatedItems(), - 'page' => BackendUtility::getRecord('pages', $pageId), - 'users' => $this->getUsers(), - 'fileTypes' => $this->getFileTypes(), - 'filter' => $filter, - 'statistic' => new Statistic($logEntries), - 'paginator' => $paginator, - 'pagination' => $pagination, - 'totalResultCount' => count($logEntries), - ]); - - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $this->createMenu(); - $moduleTemplate->setContent($this->view->render()); - return $this->htmlResponse($moduleTemplate->renderContent()); + return $filter ?: GeneralUtility::makeInstance(Filter::class); } /** - * Set up the doc header properly here + * Save current filter object in be user settings (uC) */ - public function initializeView(): void + protected function persistFilterInBeUserData(Filter $filter): void { - $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); - $pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css'); - $this->createMenu(); + $moduleData = $this->request->getAttribute('moduleData'); + $moduleData->set('filter', serialize($filter)); + $this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray()); } /** - * Create menu + * In case the script execution fails, because the user requested too many results + * (memory exhaustion in php), reset the filters in be user settings, so + * the belog can be accessed again in the next call. */ - private function createMenu(): void + protected function resetFilterOnMemoryExhaustionError(): void { - $moduleTemplate = $this->moduleTemplateFactory->create($this->request); - $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu(); - $menu->setIdentifier('secure_downloads'); - - if ((int)GeneralUtility::_GP('id') !== 0) { - $this->addMenuItems($menu); - } - - $this->view->assign('action', $this->request->getControllerActionName()); - $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu); + $reservedMemory = new \SplFixedArray(187500); // 3M + register_shutdown_function(function () use (&$reservedMemory): void { + $reservedMemory = null; // free the reserved memory + $error = error_get_last(); + if (str_contains($error['message'] ?? '', 'Allowed memory size of')) { + $filter = GeneralUtility::makeInstance(Filter::class); + $this->persistFilterInBeUserData($filter); + } + }); } - /** - * Adds menu options to the select menu - * - * @param Menu $menu The Menu object - */ - protected function addMenuItems(Menu &$menu): void + protected function getBackendUser(): BackendUserAuthentication { - $controllerName = $this->request->getControllerName(); - $controllerActionName = $this->request->getControllerActionName(); - $actions = [ - ['controller' => 'Log', 'action' => 'show', 'label' => 'Show by Page'], - ['controller' => 'Log', 'action' => 'list', 'label' => 'Overview'], - ]; - - foreach ($actions as $action) { - $isActive = $controllerName === $action['controller'] && $controllerActionName === $action['action']; - - $href = $this->getUriBuilder()->reset()->uriFor( - $action['action'], - [], - $action['controller'] - ); - - $item = $menu->makeMenuItem()->setTitle($action['label'])->setHref($href)->setActive($isActive); - $menu->addMenuItem($item); - } - } - - /** - * @return UriBuilder The URI builder - */ - protected function getUriBuilder(): UriBuilder - { - $uriBuilder = $this->objectManager->get(UriBuilder::class); - $uriBuilder->setRequest($this->request); - - return $uriBuilder; + return $GLOBALS['BE_USER']; } } diff --git a/Classes/Domain/Model/Log.php b/Classes/Domain/Model/Log.php index 95dda70..878a2b8 100644 --- a/Classes/Domain/Model/Log.php +++ b/Classes/Domain/Model/Log.php @@ -14,6 +14,7 @@ * ***/ +use Doctrine\DBAL\Exception; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; @@ -75,6 +76,9 @@ public function setTstamp(int $tstamp): void $this->tstamp = $tstamp; } + /** + * @throws Exception + */ public function getUserObject(): ?array { if ($this->user !== null && $this->user !== 0) { @@ -85,7 +89,7 @@ public function getUserObject(): ?array ->from('fe_users') ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($this->user, \PDO::PARAM_INT))) ->executeQuery() - ->fetch(); + ->fetchAssociative(); } return null; diff --git a/Classes/Domain/Repository/LogRepository.php b/Classes/Domain/Repository/LogRepository.php index 9cdd92e..1f98230 100644 --- a/Classes/Domain/Repository/LogRepository.php +++ b/Classes/Domain/Repository/LogRepository.php @@ -18,6 +18,7 @@ use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter; use Leuchtfeuer\SecureDownloads\Domain\Transfer\Token\AbstractToken; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException; @@ -94,7 +95,7 @@ protected function applyFilter(QueryInterface &$query, Filter $filter): void $this->applyEqualPropertyToFilter((int)$filter->getPageId(), 'page', $query, $constraints); if (count($constraints) > 0) { - $query->matching($query->logicalAnd($constraints)); + $query->matching($query->logicalAnd(...$constraints)); } } @@ -170,10 +171,11 @@ protected function applyEqualPropertyToFilter(int $property, string $propertyNam /** * Creates a log entry in the database. * - * @param AbstractToken $token The token containing information that should be logged - * @param int $fileSize The file size of the file that should be logged - * @param string $mimeType The mime type of the file that should be logged - * @param int $user The ID of the user that downloaded the file + * @param AbstractToken $token The token containing information that should be logged + * @param int $fileSize The file size of the file that should be logged + * @param string $mimeType The mime type of the file that should be logged + * @param int $user The ID of the user that downloaded the file + * @throws ResourceDoesNotExistException */ public function logDownload(AbstractToken $token, int $fileSize, string $mimeType, int $user): void { diff --git a/Classes/Domain/Repository/StorageRepository.php b/Classes/Domain/Repository/StorageRepository.php index 1b327b7..e975fc6 100644 --- a/Classes/Domain/Repository/StorageRepository.php +++ b/Classes/Domain/Repository/StorageRepository.php @@ -14,6 +14,7 @@ * ***/ +use Doctrine\DBAL\Exception; use Leuchtfeuer\SecureDownloads\Resource\Driver\SecureDownloadsDriver; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -26,7 +27,7 @@ class StorageRepository extends \TYPO3\CMS\Core\Resource\StorageRepository * Creates the "Secure Downloads" file storage object if not exists and if the extension configuration option is enabled. This * method will also create the directory containing the assets and puts an .htaccess file into that directory. */ - public function createSecureDownloadStorage() + public function createSecureDownloadStorage(): void { $path = sprintf('%s/%s', Environment::getPublicPath(), SecureDownloadsDriver::BASE_PATH); @@ -62,7 +63,7 @@ public function createSecureDownloadStorage() * * @return int id of the inserted record */ - public function createLocalStorage($name, $basePath, $pathType, $description = '', $default = false) + public function createLocalStorage($name, $basePath, $pathType, $description = '', $default = false): int { $storageId = parent::createLocalStorage($name, $basePath, $pathType, $description, $default); @@ -83,6 +84,7 @@ public function createLocalStorage($name, $basePath, $pathType, $description = ' * @param string $storageType The identifier of the storage. * * @return ResourceStorage[] + * @throws Exception */ public function findByStorageType($storageType): array { @@ -93,7 +95,7 @@ public function findByStorageType($storageType): array ->from($this->table) ->where($queryBuilder->expr()->eq('driver', $queryBuilder->createNamedParameter(SecureDownloadsDriver::DRIVER_SHORT_NAME))) ->executeQuery() - ->fetchAll(); + ->fetchAllAssociative(); } /** diff --git a/Classes/EventListener/SecureDownloadsEventListener.php b/Classes/EventListener/SecureDownloadsEventListener.php index 29eda46..7eaff8e 100644 --- a/Classes/EventListener/SecureDownloadsEventListener.php +++ b/Classes/EventListener/SecureDownloadsEventListener.php @@ -60,7 +60,7 @@ public function onResourceStorageEmitPreGeneratePublicUrlSignal(GeneratePublicUr } $publicUrl = $driver->getPublicUrl($resource->getIdentifier()) ?? ''; if ($originalPathShouldBeSecured || $driver instanceof SecureDownloadsDriver || $this->secureDownloadService->pathShouldBeSecured($publicUrl)) { - $securedUrl = $this->getSecuredUrl($publicUrl); + $securedUrl = $this->secureDownloadService->getResourceUrl($publicUrl); $event->setPublicUrl($securedUrl); } } catch (Exception $exception) { @@ -98,15 +98,4 @@ public function onIconFactoryEmitBuildIconForResourceSignal(ModifyIconForResourc $event->setOverlayIdentifier($overlayIdentifier ?? $event->getOverlayIdentifier()); } - - /** - * Returns the encrypted URL. - * - * @param string $publicUrl The public URL to the file. - * @return string The secured URL - */ - protected function getSecuredUrl(string $publicUrl): string - { - return $this->secureDownloadService->getResourceUrl($publicUrl); - } } diff --git a/Classes/Middleware/TokenRefreshMiddleware.php b/Classes/Middleware/TokenRefreshMiddleware.php index 5a6e54b..60f3a62 100644 --- a/Classes/Middleware/TokenRefreshMiddleware.php +++ b/Classes/Middleware/TokenRefreshMiddleware.php @@ -15,6 +15,7 @@ ***/ use Firebase\JWT\JWT; +use Firebase\JWT\Key; use Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -86,8 +87,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if (preg_match_all($pattern, $content, $foundJwtTokens)) { foreach ($foundJwtTokens[1] as $foundJwtToken) { try { - $headers = (object) ['alg' => 'HS256']; - $data = JWT::decode($foundJwtToken, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $headers); + $data = JWT::decode($foundJwtToken, new Key($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256')); if ((int)$data->user !== $currentUserId) { $data->user = $currentUserId; $newToken = JWT::encode((array)$data, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], 'HS256'); diff --git a/Configuration/Backend/Modules.php b/Configuration/Backend/Modules.php new file mode 100644 index 0000000..8f970a3 --- /dev/null +++ b/Configuration/Backend/Modules.php @@ -0,0 +1,20 @@ + [ + 'parent' => 'web', + 'position' => ['after' => 'web_info'], + 'access' => 'user', + 'path' => '/module/page/secure-downloads', + 'labels' => 'LLL:EXT:secure_downloads/Resources/Private/Language/locallang_log.xlf', + 'extensionName' => 'Secure Downloads', + 'iconIdentifier' => 'tx_securedownloads-module', + 'controllerActions' => [ + LogController::class => [ + 'list', + ], + ], + ], +]; \ No newline at end of file diff --git a/Configuration/Icons.php b/Configuration/Icons.php new file mode 100644 index 0000000..66cd559 --- /dev/null +++ b/Configuration/Icons.php @@ -0,0 +1,23 @@ + [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:secure_downloads/Resources/Public/Icons/Extension.svg', + ], +]; diff --git a/Documentation/About/ChangeLog/6-0-0.rst b/Documentation/About/ChangeLog/6-0-0.rst new file mode 100755 index 0000000..c73c3ff --- /dev/null +++ b/Documentation/About/ChangeLog/6-0-0.rst @@ -0,0 +1,55 @@ +.. include:: ../../Includes.txt + +========================== +Version 6.0.0 - 2023/06/26 +========================== + +This release is a new major release. It introduces support for TYPO3 12 LTS as well as for PHP 8.1. TYPO3 11 LTS is not supported anymore. + +Download +======== + +Download this version from the `TYPO3 extension repository `__ or from +`GitHub `__. + +Added +===== +* Support for TYPO3 version 12 + +Changed +======= +* Reworked backend module + +Deprecated +========== +* - + +Removed +======= +* Backend module action 'show' + +All Changes +=========== +This is a list of all changes in this release:: + + 2023-06-21 [TASK] Move typecast to variable assignment. (Commit a2a6c98 by Sebastian Afeldt) + 2023-06-21 [BUGFIX] Test 'whenALinkWithAnOtherUserIDofTheCurrentUserLinkResponseBodyIsModified' fails. Thanks to J. Peter M. Schuler #163 (Commit 20fbdbf by Sebastian Afeldt) + 2023-06-21 [TASK] Update testing framework. (Commit ca9177c by Sebastian Afeldt) + 2023-06-20 [TASK] Undo removal of extension configuration 'log'. (Commit 0ba273a by Sebastian Afeldt) + 2023-06-20 [TASK] Remove extension configuration 'Log Module (logging.log [boolean])'. (Commit 0587085 by Sebastian Afeldt) + 2023-06-20 [TASK] Refactor Traffic Log backend module and its templates. (Commit f5ebf85 by Sebastian Afeldt) + 2023-06-20 [TASK] Refactor code for PHP >=7.4. Thanks to Oliver Kleinecke (Commit 0d156c3 by Sebastian Afeldt) + 2023-06-16 [TASK] Support TYPO3 12.4. Thanks to sk-foresite #162 (Commit a605ecf by Sebastian Afeldt) + +Contributors +============ +Following people have contributed to this release: + +* 'sk-foresite ' +* 'J. Peter M. Schuler ' +* Oliver Kleinecke +* Marcus Balasch +* Niklas Grieger +* Sebastian Afeldt + +Thank you very much for your support. The next drink is on us! 🍻 diff --git a/Documentation/About/ChangeLog/Index.rst b/Documentation/About/ChangeLog/Index.rst index 2afc802..6d4ca65 100755 --- a/Documentation/About/ChangeLog/Index.rst +++ b/Documentation/About/ChangeLog/Index.rst @@ -17,6 +17,7 @@ List of versions :titlesonly: :glob: + 6-0-0 5-0-1 5-0-0 4-1-6 diff --git a/Documentation/About/Index.rst b/Documentation/About/Index.rst index f8141e1..607c826 100755 --- a/Documentation/About/Index.rst +++ b/Documentation/About/Index.rst @@ -32,16 +32,14 @@ As a complementary measure, you will of course need to configure your web server Compatibility ============= -We are currently supporting following TYPO3 versions:

+We are currently supporting following TYPO3 versions: -.. csv-table:: Version Matrix - :header: "Extension Version", "TYPO3 v10 Support", "TYPO3 v9 Support" +.. csv-table:: Version Matrix - Supported Versions + :header: "Extension Version", "TYPO3 v12", "TYPO3 v11", "TYPO3 v10" :align: center - "5.x", "🙋‍♂️", "🙅‍♀️" - "4.x", "🙋‍♂️", "🙋‍♂️" - -Version 5 is an upcoming release. Its package name has been changed to `leuchtfeuer/secure-downloads`. + "6.x", "yes", "no", "no" + "5.x", "no", "yes", "yes" .. _about-compatibility-outdatedVersions: @@ -49,17 +47,18 @@ Outdated Versions ----------------- For the following versions no more free bug fixes and new features will be provided by the authors: - -.. csv-table:: Version Matrix - :header: "Extension Version", "TYPO3 v9", "TYPO3 v8", "TYPO3 v7", "TYPO3 v6.2", "TYPO3 v4.5" +.. csv-table:: Version Matrix - Outdated Versions + :header: "Extension Version", "TYPO3 v10", "TYPO3 v9", "TYPO3 v8", "TYPO3 v7", "TYPO3 v6.2", "TYPO3 v4.5" :align: center - "3.x", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️", "🙅‍♀️", "🙅‍♀️" - "2.0.4 - 2.x", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️", "🙅‍♀️" - "2.0.0 - 2.0.3", "🙅‍♀️", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️", "🙅‍♀️" - "1.x", "🙅‍♀️", "🙅‍♀️", "🙅‍♀️", "🙋‍♂️", "🙋‍♂️" + "4.x", "yes", "yes", "no", "no", "no", "no" + "3.x", "no", "yes", "yes", "no", "no", "no" + "2.0.4 - 2.x", "no", "no", "yes", "yes", "no", "no" + "2.0.0 - 2.0.3", "no", "no", "yes", "yes", "yes", "no" + "1.x", "no", "no", "no", "no", "no", "yes" Version 1 was released as `EXT:naw_securedl `__ or `typo3-ter/naw-securedl`. +Its package name has been changed to `leuchtfeuer/secure-downloads` since Version 5. .. _about-links: diff --git a/Resources/Private/Layouts/Default.html b/Resources/Private/Layouts/Default.html index 7600649..b26fec7 100644 --- a/Resources/Private/Layouts/Default.html +++ b/Resources/Private/Layouts/Default.html @@ -1,27 +1,12 @@ - - -
- - - - -
- {statistic.traffic -> f:format.bytes()} - {statistic.from -> f:format.date(format: '{f:translate(key: \'dateformat\')}')} - {statistic.till -> f:format.date(format: '{f:translate(key: \'dateformat\')}')} - - {f:translate(key: 'module.subheading', arguments: '{0: totalResultCount, 1: traffic, 2: from, 3: till}') -> f:format.html()} -
- - -
- -
-
-
- - - - - -
+ + +
+
+ +
+ +
+
\ No newline at end of file diff --git a/Resources/Private/Partials/Backend/Filter.html b/Resources/Private/Partials/Backend/Filter.html index ec892c2..f956b3b 100644 --- a/Resources/Private/Partials/Backend/Filter.html +++ b/Resources/Private/Partials/Backend/Filter.html @@ -1,68 +1,79 @@ - + + +
-
+
- + +
-
+
-
- -
- -
-
- -
-
- -
+
+
+ + +
+
+ +
- -
-
-
- - - -
-
- -
+
+
+ + + +
+
+
- - -
diff --git a/Resources/Private/Partials/Backend/View.html b/Resources/Private/Partials/Backend/View.html index 525dc22..b2bdddf 100644 --- a/Resources/Private/Partials/Backend/View.html +++ b/Resources/Private/Partials/Backend/View.html @@ -1,16 +1,21 @@ - + + +
- - - - - - - - + + + + + + + + - +
diff --git a/Resources/Private/Templates/Log/List.html b/Resources/Private/Templates/Log/List.html index 25cb689..de8455b 100644 --- a/Resources/Private/Templates/Log/List.html +++ b/Resources/Private/Templates/Log/List.html @@ -1,7 +1,58 @@ + + - -

- -

-
+ + + + + +
+

+ + + + + + [{page.uid}] + + +

+ + + +
+ + + + {f:translate(key: 'module.subheading', arguments: '{0: totalResultCount, 1: traffic, 2: from, 3: till}') -> f:format.raw()} +
+ + + + +
+
+ +
+

Logging is disabled

+

If you want to enable logging, goto extension configuration of EXT:secure_downloads + and activate the checkbox 'Log Module' [log] + within the configuration tab 'Logging'.
Inform a system maintainer in case you + don't have access to the extension configuration.

+
+
+
+ + +
+ + + + + +
+
\ No newline at end of file diff --git a/Resources/Private/Templates/Log/Show.html b/Resources/Private/Templates/Log/Show.html deleted file mode 100644 index fb9ab7f..0000000 --- a/Resources/Private/Templates/Log/Show.html +++ /dev/null @@ -1,7 +0,0 @@ - - - -

- -

-
diff --git a/composer.json b/composer.json index e6dc317..49dbe0e 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ } }, "require-dev": { - "typo3/testing-framework": "^6.16" + "typo3/testing-framework": "^8.0.1" }, "config": { "allow-plugins": { diff --git a/ext_tables.php b/ext_tables.php index 78f2e1b..0b9fd9c 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -3,23 +3,6 @@ call_user_func( function ($extensionKey) { - // Register the backend module if the log option is set in extension configuration - if ((new \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration())->isLog()) { - \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( - $extensionKey, - 'web', - 'TrafficLog', - '10', - [ - \Leuchtfeuer\SecureDownloads\Controller\LogController::class => 'show,list', - ], [ - 'access' => 'user,group', - 'icon' => 'EXT:secure_downloads/Resources/Public/Icons/Extension.svg', - 'labels' => 'LLL:EXT:secure_downloads/Resources/Private/Language/locallang_log.xlf', - ] - ); - } - // Create resource storage if ((new \Leuchtfeuer\SecureDownloads\Domain\Transfer\ExtensionConfiguration())->isCreateFileStorage()) { $storageRepositoryClass = \Leuchtfeuer\SecureDownloads\Domain\Repository\StorageRepository::class; From 60a776b86b67f4fd07c719491b90b10557d3a6d8 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Mon, 26 Jun 2023 13:49:25 +0200 Subject: [PATCH 11/15] [TASK] Use JS date-time-picker from cms-belog. TER-108 TER-122 --- Classes/Controller/LogController.php | 2 +- Configuration/JavaScriptModules.php | 11 ++++ .../Private/Partials/Backend/Filter.html | 56 ++++++++++++----- Resources/Private/Templates/Log/List.html | 7 +++ .../tx_securedownloads_domain_model_log.png | Bin 313 -> 0 bytes Resources/Public/JavaScript/sdl-log.js | 58 ++++++++++++++++++ 6 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 Configuration/JavaScriptModules.php delete mode 100644 Resources/Public/Icons/tx_securedownloads_domain_model_log.png create mode 100644 Resources/Public/JavaScript/sdl-log.js diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 56847a5..ed3af52 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -75,7 +75,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; - $pageId = (int) $this->request->getQueryParams()['id'] ?? 0; + $pageId = (int) (array_key_exists('id', $this->request->getQueryParams()) ? $this->request->getQueryParams()['id'] : 0); $filter->setPageId($pageId); $logEntries = $this->logRepository->findByFilter($filter); diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 0000000..9cbeb56 --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,11 @@ + [ + 'backend', + 'core', + ], + 'imports' => [ + '@leuchtfeuer/secure-downloads/' => 'EXT:secure_downloads/Resources/Public/JavaScript/', + ], +]; diff --git a/Resources/Private/Partials/Backend/Filter.html b/Resources/Private/Partials/Backend/Filter.html index f956b3b..9f265a4 100644 --- a/Resources/Private/Partials/Backend/Filter.html +++ b/Resources/Private/Partials/Backend/Filter.html @@ -42,26 +42,50 @@
-
- - +
+ +
+ + + +
+
diff --git a/Resources/Private/Templates/Log/List.html b/Resources/Private/Templates/Log/List.html index de8455b..78acaf1 100644 --- a/Resources/Private/Templates/Log/List.html +++ b/Resources/Private/Templates/Log/List.html @@ -8,6 +8,13 @@ + + +
diff --git a/Resources/Public/Icons/tx_securedownloads_domain_model_log.png b/Resources/Public/Icons/tx_securedownloads_domain_model_log.png deleted file mode 100644 index f182a8cb9f88605eaf8f99f09e754bca6584dfeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%83*`;xPF@I@V-jm-?#UFo}B-(Ea5}5!mrEg|GvHdeMiIB z^#wmq&Hp?%;P;*VKMwYP>@oQN|Npx}hUYI@NWmlv60}Ud;{Wsv{HK5;=fsf7l { + this.clearableElements = document.querySelectorAll(".t3js-clearable"), this.dateTimePickerElements = document.querySelectorAll(".t3js-datetimepicker"), this.elementBrowserElements = document.querySelectorAll(".t3js-element-browser"), this.initializeClearableElements(), this.initializeDateTimePickerElements(), this.initializeElementBrowserElements(), this.initializeElementBrowserEventListener() + })) + } + + initializeClearableElements() { + this.clearableElements.forEach((e => e.clearable())) + } + + initializeDateTimePickerElements() { + this.dateTimePickerElements.forEach((e => DateTimePicker.initialize(e))) + } + + initializeElementBrowserElements() { + this.elementBrowserElements.forEach((e => { + const t = document.getElementById(e.dataset.triggerFor); + e.dataset.params = t.name + "|||pages", e.addEventListener("click", (e => { + e.preventDefault(); + const t = e.currentTarget; + Modal.advanced({ + type: Modal.types.iframe, + content: t.dataset.target + "&mode=" + t.dataset.mode + "&bparams=" + t.dataset.params, + size: Modal.sizes.large + }) + })) + })) + } + + initializeElementBrowserEventListener() { + window.addEventListener("message", (e => { + if (!MessageUtility.verifyOrigin(e.origin) || "typo3:elementBrowser:elementAdded" !== e.data.actionName || "string" != typeof e.data.fieldName || "string" != typeof e.data.value) return; + const t = document.querySelector('input[name="' + e.data.fieldName + '"]'); + t && (t.value = e.data.value.split("_").pop()) + })) + } +} + +export default new SdlLog; \ No newline at end of file From 3c67eb5fda1c2664134c9562410ab83048f188e9 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Mon, 26 Jun 2023 15:30:02 +0200 Subject: [PATCH 12/15] [TASK] Simplify code. TER-108 TER-122 --- Classes/Controller/LogController.php | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index ed3af52..e261d20 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -32,22 +32,10 @@ class LogController extends ActionController { - /** - * @var LogRepository $logRepository - */ - protected LogRepository $logRepository; - - /** - * @var ModuleTemplateFactory - */ - protected ModuleTemplateFactory $moduleTemplateFactory; - public function __construct( - ModuleTemplateFactory $moduleTemplateFactory, - LogRepository $logRepository, + protected ModuleTemplateFactory $moduleTemplateFactory, + protected LogRepository $logRepository, ) { - $this->moduleTemplateFactory = $moduleTemplateFactory; - $this->logRepository = $logRepository; } /** @@ -75,7 +63,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; - $pageId = (int) (array_key_exists('id', $this->request->getQueryParams()) ? $this->request->getQueryParams()['id'] : 0); + $pageId = (int)(array_key_exists('id', $this->request->getQueryParams()) ? $this->request->getQueryParams()['id'] : 0); $filter->setPageId($pageId); $logEntries = $this->logRepository->findByFilter($filter); @@ -83,7 +71,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $this->resetFilterOnMemoryExhaustionError(); $itemsPerPage = 20; - $currentPage = (int) array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; + $currentPage = (int)array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); $pagination = new SimplePagination($paginator); From ff27a1a09fc1c5ac7c141435ab2c699b6d041aa4 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt <33094668+bmafeldt@users.noreply.github.com> Date: Tue, 27 Jun 2023 16:20:36 +0200 Subject: [PATCH 13/15] Use JavaScript date time picker to get consistent browser support. TER-108 (#167) * [TASK][WIP] Refactor code for PHP >=7.4. * [TASK][WIP] Refactor Traffic Log backend module and its templates. - Use be_user module data instead of PHP Session to store filter settings. - Remove 'showAction', as this was almost the same as 'listAction'. Therefore, traffic data per page can be viewed by select a page, all traffic can be viewed per click on TYPO3 root page UID 0. - Removed/replaced deprecated functions. - Icon factory added. - Module registration moved. * [TASK][WIP] Remove extension configuration 'Log Module (logging.log [boolean])'. - The module will now always be active. * [TASK] Undo removal of extension configuration 'log'. TER-108 TER-116 * [TASK] Update testing framework. TER-108 TER-119 * [BUGFIX] Test 'whenALinkWithAnOtherUserIDofTheCurrentUserLinkResponseBodyIsModified' fails. TER-108 TER-119 This is exactly the '[BUGFIX] JWT::decode method signature not respected #163' - Thanks to https://github.com/jpmschuler * [TASK] Move typecast to variable assignment. TER-108 TER-116 * [TASK] Update extension documentation. TER-108 TER-111 * [TASK] Use JS date-time-picker from cms-belog. TER-108 TER-122 * [TASK] Simplify code. TER-108 TER-122 --- Classes/Controller/LogController.php | 20 ++---- Configuration/JavaScriptModules.php | 11 ++++ .../Private/Partials/Backend/Filter.html | 56 ++++++++++++----- Resources/Private/Templates/Log/List.html | 6 ++ .../tx_securedownloads_domain_model_log.png | Bin 313 -> 0 bytes Resources/Public/JavaScript/sdl-log.js | 58 ++++++++++++++++++ 6 files changed, 119 insertions(+), 32 deletions(-) create mode 100644 Configuration/JavaScriptModules.php delete mode 100644 Resources/Public/Icons/tx_securedownloads_domain_model_log.png create mode 100644 Resources/Public/JavaScript/sdl-log.js diff --git a/Classes/Controller/LogController.php b/Classes/Controller/LogController.php index 56847a5..e261d20 100644 --- a/Classes/Controller/LogController.php +++ b/Classes/Controller/LogController.php @@ -32,22 +32,10 @@ class LogController extends ActionController { - /** - * @var LogRepository $logRepository - */ - protected LogRepository $logRepository; - - /** - * @var ModuleTemplateFactory - */ - protected ModuleTemplateFactory $moduleTemplateFactory; - public function __construct( - ModuleTemplateFactory $moduleTemplateFactory, - LogRepository $logRepository, + protected ModuleTemplateFactory $moduleTemplateFactory, + protected LogRepository $logRepository, ) { - $this->moduleTemplateFactory = $moduleTemplateFactory; - $this->logRepository = $logRepository; } /** @@ -75,7 +63,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0; - $pageId = (int) $this->request->getQueryParams()['id'] ?? 0; + $pageId = (int)(array_key_exists('id', $this->request->getQueryParams()) ? $this->request->getQueryParams()['id'] : 0); $filter->setPageId($pageId); $logEntries = $this->logRepository->findByFilter($filter); @@ -83,7 +71,7 @@ public function listAction(?Filter $filter = null): ResponseInterface $this->resetFilterOnMemoryExhaustionError(); $itemsPerPage = 20; - $currentPage = (int) array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; + $currentPage = (int)array_key_exists('currentPage', $this->request->getQueryParams()) && $this->request->getQueryParams()['currentPage'] > 0 ? $this->request->getQueryParams()['currentPage'] : 1; $paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage); $pagination = new SimplePagination($paginator); diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 0000000..9cbeb56 --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,11 @@ + [ + 'backend', + 'core', + ], + 'imports' => [ + '@leuchtfeuer/secure-downloads/' => 'EXT:secure_downloads/Resources/Public/JavaScript/', + ], +]; diff --git a/Resources/Private/Partials/Backend/Filter.html b/Resources/Private/Partials/Backend/Filter.html index f956b3b..9f265a4 100644 --- a/Resources/Private/Partials/Backend/Filter.html +++ b/Resources/Private/Partials/Backend/Filter.html @@ -42,26 +42,50 @@
-
- - +
+ +
+ + + +
+
diff --git a/Resources/Private/Templates/Log/List.html b/Resources/Private/Templates/Log/List.html index de8455b..7fcde3d 100644 --- a/Resources/Private/Templates/Log/List.html +++ b/Resources/Private/Templates/Log/List.html @@ -8,6 +8,12 @@ + +
diff --git a/Resources/Public/Icons/tx_securedownloads_domain_model_log.png b/Resources/Public/Icons/tx_securedownloads_domain_model_log.png deleted file mode 100644 index f182a8cb9f88605eaf8f99f09e754bca6584dfeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%83*`;xPF@I@V-jm-?#UFo}B-(Ea5}5!mrEg|GvHdeMiIB z^#wmq&Hp?%;P;*VKMwYP>@oQN|Npx}hUYI@NWmlv60}Ud;{Wsv{HK5;=fsf7l { + this.clearableElements = document.querySelectorAll(".t3js-clearable"), this.dateTimePickerElements = document.querySelectorAll(".t3js-datetimepicker"), this.elementBrowserElements = document.querySelectorAll(".t3js-element-browser"), this.initializeClearableElements(), this.initializeDateTimePickerElements(), this.initializeElementBrowserElements(), this.initializeElementBrowserEventListener() + })) + } + + initializeClearableElements() { + this.clearableElements.forEach((e => e.clearable())) + } + + initializeDateTimePickerElements() { + this.dateTimePickerElements.forEach((e => DateTimePicker.initialize(e))) + } + + initializeElementBrowserElements() { + this.elementBrowserElements.forEach((e => { + const t = document.getElementById(e.dataset.triggerFor); + e.dataset.params = t.name + "|||pages", e.addEventListener("click", (e => { + e.preventDefault(); + const t = e.currentTarget; + Modal.advanced({ + type: Modal.types.iframe, + content: t.dataset.target + "&mode=" + t.dataset.mode + "&bparams=" + t.dataset.params, + size: Modal.sizes.large + }) + })) + })) + } + + initializeElementBrowserEventListener() { + window.addEventListener("message", (e => { + if (!MessageUtility.verifyOrigin(e.origin) || "typo3:elementBrowser:elementAdded" !== e.data.actionName || "string" != typeof e.data.fieldName || "string" != typeof e.data.value) return; + const t = document.querySelector('input[name="' + e.data.fieldName + '"]'); + t && (t.value = e.data.value.split("_").pop()) + })) + } +} + +export default new SdlLog; \ No newline at end of file From 857fb90f0f9d8914a1087991a55b34a481104d81 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt Date: Wed, 28 Jun 2023 13:43:28 +0200 Subject: [PATCH 14/15] [TASK] Update readme. TER-108 TER-122 --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0caaa97..106fd61 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,10 @@ As a complementary measure, you will of course need to configure your web server ## Requirements We are currently supporting following TYPO3 versions: -| Extension Version | TYPO3 v11 | TYPO3 v10 | -| ------------------------------------------------------------------------------ |-----------|-----------| -| [5.x](https://github.com/Leuchtfeuer/typo3-secure-downloads) 1) | x | x | +| Extension Version | TYPO3 v12 | TYPO3 v11 | TYPO3 v10 | +|--------------------------------------------------------------------------|-----------|-----------|-----------| +| 6.x | x | - | - | +| 5.x 1) | - | x | x | * 1) Upcoming release as `leuchtfeuer/secure-downloads` (vendor name changed). From 4df1ba45d507a35e43a422f58c886cc824147674 Mon Sep 17 00:00:00 2001 From: Sebastian Afeldt <33094668+bmafeldt@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:54:33 +0200 Subject: [PATCH 15/15] [TASK] Update readme. TER-108 TER-123 (#169) --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0caaa97..88a7fc5 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,10 @@ As a complementary measure, you will of course need to configure your web server ## Requirements We are currently supporting following TYPO3 versions: -| Extension Version | TYPO3 v11 | TYPO3 v10 | -| ------------------------------------------------------------------------------ |-----------|-----------| -| [5.x](https://github.com/Leuchtfeuer/typo3-secure-downloads) 1) | x | x | +| Extension Version | TYPO3 v12 | TYPO3 v11 | TYPO3 v10 | +|-------------------|-----------|-----------|-----------| +| 6.x | x | - | - | +| 5.x 1) | - | x | x | * 1) Upcoming release as `leuchtfeuer/secure-downloads` (vendor name changed).