Skip to content

Commit

Permalink
Merge pull request #170 from Leuchtfeuer/release-6.x
Browse files Browse the repository at this point in the history
Release 6.x
  • Loading branch information
balasch committed Jun 30, 2023
2 parents f23876c + 45ac00c commit 3aff949
Show file tree
Hide file tree
Showing 34 changed files with 567 additions and 386 deletions.
201 changes: 69 additions & 132 deletions Classes/Controller/LogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,92 +14,88 @@
*
***/

use Doctrine\DBAL\Exception;
use Leuchtfeuer\SecureDownloads\Domain\Repository\LogRepository;
use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter;
use Leuchtfeuer\SecureDownloads\Domain\Transfer\Statistic;
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\BackendTemplateView;
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\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';
public function __construct(
protected ModuleTemplateFactory $moduleTemplateFactory,
protected LogRepository $logRepository,
) {
}

/**
* @var BackendTemplateView
* @return ResponseInterface
*/
protected $view;

protected $defaultViewObjectName = BackendTemplateView::class;

protected $logRepository;

public function __construct(LogRepository $logRepository)
public function initializeAction(): ResponseInterface
{
$this->logRepository = $logRepository;
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css');
return $this->htmlResponse('');
}

/**
* @throws NoSuchArgumentException
* @param Filter|null $filter The filter object
* @return ResponseInterface
* @throws Exception
*/
public function initializeAction(): void
public function listAction(?Filter $filter = null): 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()));
$filter = new Filter();
} elseif ($filter === null) {
$filter = $this->getFilterFromBeUserData();
}

if ($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY) === null) {
$GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize(new Filter()));
}
}
$extensionConfigurationLogging = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('secure_downloads', 'log') ?? 0;

/**
* @param Filter|null $filter The filter object
*/
public function listAction(?Filter $filter = null): void
{
$filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter());
$filter->setPageId(0);
$pageId = (int)(array_key_exists('id', $this->request->getQueryParams()) ? $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,
'statistic' => new Statistic($logEntries),
'paginator' => $paginator,
'pagination' => $pagination,
'totalResultCount' => count($logEntries),
'isRoot' => $pageId == 0,
]);
return $moduleTemplate->renderResponse('List');
}

/**
* @return array Array containing all users that have downloaded files
* @throws Exception
*/
private function getUsers(): array
{
Expand All @@ -111,12 +107,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()
->fetchAll();
->executeQuery()
->fetchAllAssociative();
}

/**
* @return array Array containing all used file types
* @throws Exception
*/
private function getFileTypes(): array
{
Expand All @@ -125,114 +122,54 @@ private function getFileTypes(): array
return $queryBuilder
->select('media_type')
->from('tx_securedownloads_domain_model_log')
->groupBy('media_type')
->orderBy('media_type', 'ASC')
->execute()
->fetchAll();
->groupBy('media_type')->orderBy('media_type', 'ASC')
->executeQuery()
->fetchAllAssociative();
}

/**
* @param Filter|null $filter The filter object
* @throws StopActionException
* Get module states (the filter object) from user data
*/
public function showAction(?Filter $filter = null): void
protected function getFilterFromBeUserData(): Filter
{
$pageId = (int)GeneralUtility::_GP('id');

if ($pageId === 0) {
$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),
]);
}

/**
* Set up the doc header properly here
*/
public function initializeView(ViewInterface $view): void
{
parent::initializeView($view);

$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->addCssFile('EXT:secure_downloads/Resources/Public/Styles/Styles.css');
$this->createMenu();
return $filter ?: GeneralUtility::makeInstance(Filter::class);
}

/**
* Create menu
* Save current filter object in be user settings (uC)
*/
private function createMenu(): void
protected function persistFilterInBeUserData(Filter $filter): void
{
$menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
$menu->setIdentifier('secure_downloads');

if ((int)GeneralUtility::_GP('id') !== 0) {
$this->addMenuItems($menu);
}

$this->view->assign('action', $this->request->getControllerActionName());
$this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
$moduleData = $this->request->getAttribute('moduleData');
$moduleData->set('filter', serialize($filter));
$this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray());
}

/**
* Adds menu options to the select menu
*
* @param Menu $menu The Menu object
* 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.
*/
protected function addMenuItems(Menu &$menu): void
protected function resetFilterOnMemoryExhaustionError(): 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'];
}
}
8 changes: 6 additions & 2 deletions Classes/Domain/Model/Log.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -84,8 +88,8 @@ public function getUserObject(): ?array
->select('*')
->from('fe_users')
->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($this->user, \PDO::PARAM_INT)))
->execute()
->fetch();
->executeQuery()
->fetchAssociative();
}

return null;
Expand Down
14 changes: 8 additions & 6 deletions Classes/Domain/Repository/LogRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
}

Expand Down Expand Up @@ -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
{
Expand All @@ -195,6 +197,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();
}
}
Loading

0 comments on commit 3aff949

Please sign in to comment.