Files
gutachten_t3/typo3/sysext/belog/Classes/Controller/BackendLogController.php
T
2026-10-19 08:18:39 +02:00

371 lines
14 KiB
PHP

<?php
namespace TYPO3\CMS\Belog\Controller;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\View\BackendTemplateView;
use TYPO3\CMS\Belog\Domain\Model\Constraint;
use TYPO3\CMS\Belog\Domain\Model\LogEntry;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* Abstract class to show log entries from sys_log
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
*/
class BackendLogController extends ActionController
{
/**
* @var int
*/
private const TIMEFRAME_THISWEEK = 0;
/**
* @var int
*/
private const TIMEFRAME_LASTWEEK = 1;
/**
* @var int
*/
private const TIMEFRAME_LASTSEVENDAYS = 2;
/**
* @var int
*/
private const TIMEFRAME_THISMONTH = 10;
/**
* @var int
*/
private const TIMEFRAME_LASTMONTH = 11;
/**
* @var int
*/
private const TIMEFRAME_LAST31DAYS = 12;
/**
* @var int
*/
private const TIMEFRAME_CUSTOM = 30;
/**
* @var \TYPO3\CMS\Belog\Domain\Repository\LogEntryRepository
*/
protected $logEntryRepository;
/**
* @var BackendTemplateView
*/
protected $view;
/**
* @param \TYPO3\CMS\Belog\Domain\Repository\LogEntryRepository $logEntryRepository
*/
public function injectLogEntryRepository(\TYPO3\CMS\Belog\Domain\Repository\LogEntryRepository $logEntryRepository)
{
$this->logEntryRepository = $logEntryRepository;
}
/**
* Initialize list action
*/
public function initializeListAction()
{
if (!isset($this->settings['dateFormat'])) {
$this->settings['dateFormat'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 'm-d-Y' : 'd-m-Y';
}
if (!isset($this->settings['timeFormat'])) {
$this->settings['timeFormat'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
}
$constraintConfiguration = $this->arguments->getArgument('constraint')->getPropertyMappingConfiguration();
$constraintConfiguration->allowAllProperties();
}
/**
* Show general information and the installed modules
*
* @param Constraint $constraint
* @param int $pageId
* @param string $layout
*/
public function listAction(Constraint $constraint = null, int $pageId = null, string $layout = 'Default')
{
// Constraint object handling:
// If there is none from GET, try to get it from BE user data, else create new
if ($constraint === null) {
$constraint = $this->getConstraintFromBeUserData();
} else {
$this->persistConstraintInBeUserData($constraint);
}
$constraint->setPageId($pageId);
$this->resetConstraintsOnMemoryExhaustionError();
$this->setStartAndEndTimeFromTimeSelector($constraint);
$this->forceWorkspaceSelectionIfInWorkspace($constraint);
$logEntries = $this->logEntryRepository->findByConstraint($constraint);
$groupedLogEntries = $this->groupLogEntriesByPageAndDay($logEntries, $constraint->getGroupByPage());
$this->view->assignMultiple([
'pageId' => $pageId,
'layout' => $layout,
'groupedLogEntries' => $groupedLogEntries,
'constraint' => $constraint,
'userGroups' => $this->createUserAndGroupListForSelectOptions(),
'workspaces' => $this->createWorkspaceListForSelectOptions(),
'pageDepths' => $this->createPageDepthOptions(),
]);
}
/**
* Delete all log entries that share the same message with the log entry given
* in $errorUid
*
* @param int $errorUid
*/
public function deleteMessageAction(int $errorUid)
{
/** @var \TYPO3\CMS\Belog\Domain\Model\LogEntry $logEntry */
$logEntry = $this->logEntryRepository->findByUid($errorUid);
if (!$logEntry) {
$this->addFlashMessage(LocalizationUtility::translate('actions.delete.noRowFound', 'belog'), '', AbstractMessage::WARNING);
$this->redirect('list');
}
$numberOfDeletedRows = $this->logEntryRepository->deleteByMessageDetails($logEntry);
$this->addFlashMessage(sprintf(LocalizationUtility::translate('actions.delete.message', 'belog'), $numberOfDeletedRows));
$this->redirect('list');
}
/**
* Get module states (the constraint object) from user data
*
* @return Constraint
*/
protected function getConstraintFromBeUserData()
{
$serializedConstraint = $GLOBALS['BE_USER']->getModuleData(static::class);
$constraint = null;
if (is_string($serializedConstraint) && !empty($serializedConstraint)) {
$constraint = @unserialize($serializedConstraint, ['allowed_classes' => [Constraint::class, \DateTime::class]]);
}
return $constraint ?: $this->objectManager->get(Constraint::class);
}
/**
* Save current constraint object in be user settings (uC)
*
* @param Constraint $constraint
*/
protected function persistConstraintInBeUserData(Constraint $constraint)
{
$GLOBALS['BE_USER']->pushModuleData(static::class, serialize($constraint));
}
/**
* In case the script execution fails, because the user requested too many results
* (memory exhaustion in php), reset the constraints in be user settings, so
* the belog can be accessed again in the next call.
*/
protected function resetConstraintsOnMemoryExhaustionError()
{
$reservedMemory = new \SplFixedArray(187500); // 3M
register_shutdown_function(function () use (&$reservedMemory) {
$reservedMemory = null; // free the reserved memory
$error = error_get_last();
if (strpos($error['message'], 'Allowed memory size of') !== false) {
$constraint = $this->objectManager->get(Constraint::class);
$this->persistConstraintInBeUserData($constraint);
}
});
}
/**
* Create a sorted array for day and page view from
* the query result of the sys log repository.
*
* If group by page is FALSE, pid is always -1 (will render a flat list),
* otherwise the output is split by pages.
* '12345' is a sub array to split entries by day, number is first second of day
*
* [pid][dayTimestamp][items]
*
* @param QueryResultInterface $logEntries
* @param bool $groupByPage Whether or not log entries should be grouped by page
* @return array
*/
protected function groupLogEntriesByPageAndDay(QueryResultInterface $logEntries, $groupByPage = false)
{
$targetStructure = [];
/** @var LogEntry $entry */
foreach ($logEntries as $entry) {
// Create page split list or flat list
if ($groupByPage) {
$pid = $entry->getEventPid();
} else {
$pid = -1;
}
// Create array if it is not defined yet
if (!is_array($targetStructure[$pid])) {
$targetStructure[$pid] = [];
}
// Get day timestamp of log entry and create sub array if needed
$timestampDay = strtotime(strftime('%d.%m.%Y', $entry->getTstamp()));
if (!is_array($targetStructure[$pid][$timestampDay])) {
$targetStructure[$pid][$timestampDay] = [];
}
// Add row
$targetStructure[$pid][$timestampDay][] = $entry;
}
ksort($targetStructure);
return $targetStructure;
}
/**
* Create options for the user / group drop down.
* This is not moved to a repository by intention to not mix up this 'meta' data
* with real repository work
*
* @return array Key is the option name, value its label
*/
protected function createUserAndGroupListForSelectOptions()
{
$userGroupArray = [];
// Two meta entries: 'all' and 'self'
$userGroupArray[0] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('allUsers', 'Belog');
$userGroupArray[-1] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('self', 'Belog');
// List of groups, key is gr-'uid'
$groups = \TYPO3\CMS\Backend\Utility\BackendUtility::getGroupNames();
foreach ($groups as $group) {
$userGroupArray['gr-' . $group['uid']] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('group', 'Belog') . ' ' . $group['title'];
}
// List of users, key is us-'uid'
$users = \TYPO3\CMS\Backend\Utility\BackendUtility::getUserNames();
foreach ($users as $user) {
$userGroupArray['us-' . $user['uid']] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('user', 'Belog') . ' ' . $user['username'];
}
return $userGroupArray;
}
/**
* Create options for the workspace selector
*
* @return array Key is uid of workspace, value its label
*/
protected function createWorkspaceListForSelectOptions()
{
if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
return [];
}
$workspaceArray = [];
// Two meta entries: 'all' and 'live'
$workspaceArray[-99] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('any', 'Belog');
$workspaceArray[0] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('live', 'Belog');
$workspaces = $this->objectManager->get(\TYPO3\CMS\Belog\Domain\Repository\WorkspaceRepository::class)->findAll();
/** @var \TYPO3\CMS\Belog\Domain\Model\Workspace $workspace */
foreach ($workspaces as $workspace) {
$workspaceArray[$workspace->getUid()] = $workspace->getUid() . ': ' . $workspace->getTitle();
}
return $workspaceArray;
}
/**
* If the user is in a workspace different than LIVE,
* we force to show only log entries from the selected workspace,
* and the workspace selector is not shown.
*
* @param Constraint $constraint
*/
protected function forceWorkspaceSelectionIfInWorkspace(Constraint $constraint)
{
if ($GLOBALS['BE_USER']->workspace !== 0) {
$constraint->setWorkspaceUid($GLOBALS['BE_USER']->workspace);
$this->view->assign('showWorkspaceSelector', false);
} else {
$this->view->assign('showWorkspaceSelector', true);
}
}
/**
* Create options for the 'depth of page levels' selector.
* This is shown if the module is displayed in page -> info
*
* @return array Key is depth identifier (1 = One level), value the localized select option label
*/
protected function createPageDepthOptions()
{
$options = [
0 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0', 'lang'),
1 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1', 'lang'),
2 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2', 'lang'),
3 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3', 'lang'),
4 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4', 'lang'),
999 => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi', 'lang')
];
return $options;
}
/**
* Calculate the start- and end timestamp from the different time selector options
*
* @param Constraint $constraint
*/
protected function setStartAndEndTimeFromTimeSelector(Constraint $constraint)
{
$startTime = 0;
$endTime = $GLOBALS['EXEC_TIME'];
// @TODO: Refactor this construct
switch ($constraint->getTimeFrame()) {
case self::TIMEFRAME_THISWEEK:
// This week
$week = (date('w') ?: 7) - 1;
$startTime = mktime(0, 0, 0) - $week * 3600 * 24;
break;
case self::TIMEFRAME_LASTWEEK:
// Last week
$week = (date('w') ?: 7) - 1;
$startTime = mktime(0, 0, 0) - ($week + 7) * 3600 * 24;
$endTime = mktime(0, 0, 0) - $week * 3600 * 24;
break;
case self::TIMEFRAME_LASTSEVENDAYS:
// Last 7 days
$startTime = mktime(0, 0, 0) - 7 * 3600 * 24;
break;
case self::TIMEFRAME_THISMONTH:
// This month
$startTime = mktime(0, 0, 0, date('m'), 1);
break;
case self::TIMEFRAME_LASTMONTH:
// Last month
$startTime = mktime(0, 0, 0, date('m') - 1, 1);
$endTime = mktime(0, 0, 0, date('m'), 1);
break;
case self::TIMEFRAME_LAST31DAYS:
// Last 31 days
$startTime = mktime(0, 0, 0) - 31 * 3600 * 24;
break;
case self::TIMEFRAME_CUSTOM:
$startTime = $constraint->getManualDateStart() ? $constraint->getManualDateStart()->getTimestamp() : 0;
$endTime = $constraint->getManualDateStop() ? $constraint->getManualDateStop()->getTimestamp() : 0;
if ($endTime <= $startTime) {
$endTime = $GLOBALS['EXEC_TIME'];
}
break;
default:
}
$constraint->setStartTimestamp($startTime);
$constraint->setEndTimestamp($endTime);
}
}