Passed
Push — master ( 6c08ab...5a4560 )
by
unknown
34:08 queued 20:19
created

setStartAndEndTimeFromTimeSelector()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 4
nc 8
nop 1
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Belog\Controller;
17
18
use Psr\Http\Message\ResponseInterface;
19
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Belog\Domain\Model\Constraint;
22
use TYPO3\CMS\Belog\Domain\Model\LogEntry;
23
use TYPO3\CMS\Belog\Domain\Repository\LogEntryRepository;
24
use TYPO3\CMS\Belog\Domain\Repository\WorkspaceRepository;
25
use TYPO3\CMS\Core\Messaging\AbstractMessage;
26
use TYPO3\CMS\Core\Page\PageRenderer;
27
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
30
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
31
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
32
33
/**
34
 * Show log entries from sys_log
35
 *
36
 * @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
37
 */
38
class BackendLogController extends ActionController
39
{
40
    protected ModuleTemplateFactory $moduleTemplateFactory;
41
    protected LogEntryRepository $logEntryRepository;
42
    protected WorkspaceRepository $workspaceRepository;
43
44
    public function __construct(
45
        ModuleTemplateFactory $moduleTemplateFactory,
46
        LogEntryRepository $logEntryRepository,
47
        WorkspaceRepository $workspaceRepository
48
    ) {
49
        $this->moduleTemplateFactory = $moduleTemplateFactory;
50
        $this->logEntryRepository = $logEntryRepository;
51
        $this->workspaceRepository = $workspaceRepository;
52
    }
53
54
    /**
55
     * Initialize list action
56
     */
57
    public function initializeListAction()
58
    {
59
        if (!isset($this->settings['dateFormat'])) {
60
            $this->settings['dateFormat'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?: 'd-m-Y';
61
        }
62
        if (!isset($this->settings['timeFormat'])) {
63
            $this->settings['timeFormat'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
64
        }
65
        // Static format needed for date picker (flatpickr), see BackendController::generateJavascript() and #91606
66
        $this->settings['dateTimeFormat'] = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 'h:m m-d-Y' : 'h:m d-m-Y');
67
        $constraintConfiguration = $this->arguments->getArgument('constraint')->getPropertyMappingConfiguration();
68
        $constraintConfiguration->allowAllProperties();
69
        $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
70
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/GlobalEventHandler');
71
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
72
        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Belog/BackendLog');
73
    }
74
75
    /**
76
     * Show general information and the installed modules
77
     *
78
     * @param Constraint|null $constraint
79
     * @param int|null $pageId
80
     * @param string $layout
81
     * @param string $operation
82
     * @return ResponseInterface
83
     */
84
    public function listAction(Constraint $constraint = null, int $pageId = null, string $layout = 'Default', string $operation = ''): ResponseInterface
85
    {
86
        if ($operation === 'reset-filters') {
87
            $constraint = new Constraint();
88
        } elseif ($constraint === null) {
89
            $constraint = $this->getConstraintFromBeUserData();
90
        }
91
        $this->persistConstraintInBeUserData($constraint);
92
        $constraint->setPageId($pageId);
93
        $this->resetConstraintsOnMemoryExhaustionError();
94
        $this->setStartAndEndTimeFromTimeSelector($constraint);
95
        $this->forceWorkspaceSelectionIfInWorkspace($constraint);
96
        $logEntries = $this->logEntryRepository->findByConstraint($constraint);
97
        $groupedLogEntries = $this->groupLogEntriesDay($logEntries);
98
        $this->view->assignMultiple([
99
            'pageId' => $pageId,
100
            'layout' => $layout,
101
            'groupedLogEntries' => $groupedLogEntries,
102
            'constraint' => $constraint,
103
            'userGroups' => $this->createUserAndGroupListForSelectOptions(),
104
            'workspaces' => $this->createWorkspaceListForSelectOptions(),
105
            'pageDepths' => $this->createPageDepthOptions(),
106
        ]);
107
108
        if ($layout === 'Default') {
109
            $moduleTemplate = $this->moduleTemplateFactory->create($GLOBALS['TYPO3_REQUEST']);
110
            $moduleTemplate->setTitle(LocalizationUtility::translate('LLL:EXT:belog/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'));
111
            $moduleTemplate->setContent($this->view->render());
112
            return $this->htmlResponse($moduleTemplate->renderContent());
113
        }
114
        return $this->htmlResponse();
115
    }
116
117
    /**
118
     * Delete all log entries that share the same message with the log entry given
119
     * in $errorUid
120
     *
121
     * @param int $errorUid
122
     */
123
    public function deleteMessageAction(int $errorUid)
124
    {
125
        /** @var \TYPO3\CMS\Belog\Domain\Model\LogEntry $logEntry */
126
        $logEntry = $this->logEntryRepository->findByUid($errorUid);
127
        if (!$logEntry) {
0 ignored issues
show
introduced by
$logEntry is of type TYPO3\CMS\Belog\Domain\Model\LogEntry, thus it always evaluated to true.
Loading history...
128
            $this->addFlashMessage(LocalizationUtility::translate('actions.delete.noRowFound', 'belog') ?? '', '', AbstractMessage::WARNING);
129
            $this->redirect('list');
130
        }
131
        $numberOfDeletedRows = $this->logEntryRepository->deleteByMessageDetails($logEntry);
132
        $this->addFlashMessage(sprintf(LocalizationUtility::translate('actions.delete.message', 'belog') ?? '', $numberOfDeletedRows));
133
        $this->redirect('list');
134
    }
135
136
    /**
137
     * Get module states (the constraint object) from user data
138
     *
139
     * @return Constraint
140
     */
141
    protected function getConstraintFromBeUserData()
142
    {
143
        $serializedConstraint = $GLOBALS['BE_USER']->getModuleData(static::class);
144
        $constraint = null;
145
        if (is_string($serializedConstraint) && !empty($serializedConstraint)) {
146
            $constraint = @unserialize($serializedConstraint, ['allowed_classes' => [Constraint::class, \DateTime::class]]);
147
        }
148
        return $constraint ?: GeneralUtility::makeInstance(Constraint::class);
149
    }
150
151
    /**
152
     * Save current constraint object in be user settings (uC)
153
     *
154
     * @param Constraint $constraint
155
     */
156
    protected function persistConstraintInBeUserData(Constraint $constraint)
157
    {
158
        $GLOBALS['BE_USER']->pushModuleData(static::class, serialize($constraint));
159
    }
160
161
    /**
162
     * In case the script execution fails, because the user requested too many results
163
     * (memory exhaustion in php), reset the constraints in be user settings, so
164
     * the belog can be accessed again in the next call.
165
     */
166
    protected function resetConstraintsOnMemoryExhaustionError()
167
    {
168
        $reservedMemory = new \SplFixedArray(187500); // 3M
169
        register_shutdown_function(function () use (&$reservedMemory): void {
170
            $reservedMemory = null; // free the reserved memory
171
            $error = error_get_last();
172
            if (strpos($error['message'], 'Allowed memory size of') !== false) {
173
                $constraint = GeneralUtility::makeInstance(Constraint::class);
174
                $this->persistConstraintInBeUserData($constraint);
175
            }
176
        });
177
    }
178
179
    /**
180
     * Create a sorted array for day from the query result of the sys log repository.
181
     *
182
     * pid is always -1 to render a flat list.
183
     * '12345' is a sub array to split entries by day, number is first second of day
184
     *
185
     * [pid][dayTimestamp][items]
186
     *
187
     * @param QueryResultInterface $logEntries
188
     * @return array
189
     */
190
    protected function groupLogEntriesDay(QueryResultInterface $logEntries): array
191
    {
192
        $targetStructure = [];
193
        /** @var LogEntry $entry */
194
        foreach ($logEntries as $entry) {
195
            $pid = -1;
196
            // Create array if it is not defined yet
197
            if (!is_array($targetStructure[$pid])) {
198
                $targetStructure[-1] = [];
199
            }
200
            // Get day timestamp of log entry and create sub array if needed
201
            $timestampDay = strtotime(strftime('%d.%m.%Y', $entry->getTstamp()) ?: '');
202
            if (!is_array($targetStructure[$pid][$timestampDay])) {
203
                $targetStructure[$pid][$timestampDay] = [];
204
            }
205
            // Add row
206
            $targetStructure[$pid][$timestampDay][] = $entry;
207
        }
208
        ksort($targetStructure);
209
        return $targetStructure;
210
    }
211
212
    /**
213
     * Create options for the user / group drop down.
214
     * This is not moved to a repository by intention to not mix up this 'meta' data
215
     * with real repository work
216
     *
217
     * @return array Key is the option name, value its label
218
     */
219
    protected function createUserAndGroupListForSelectOptions()
220
    {
221
        $userGroupArray = [];
222
        // Two meta entries: 'all' and 'self'
223
        $userGroupArray[0] = LocalizationUtility::translate('allUsers', 'Belog');
224
        $userGroupArray[-1] = LocalizationUtility::translate('self', 'Belog');
225
        // List of groups, key is gr-'uid'
226
        $groups = BackendUtility::getGroupNames();
227
        foreach ($groups as $group) {
228
            $userGroupArray['gr-' . $group['uid']] = LocalizationUtility::translate('group', 'Belog') . ' ' . $group['title'];
229
        }
230
        // List of users, key is us-'uid'
231
        $users = BackendUtility::getUserNames();
232
        foreach ($users as $user) {
233
            $userGroupArray['us-' . $user['uid']] = LocalizationUtility::translate('user', 'Belog') . ' ' . $user['username'];
234
        }
235
        return $userGroupArray;
236
    }
237
238
    /**
239
     * Create options for the workspace selector
240
     *
241
     * @return array Key is uid of workspace, value its label
242
     */
243
    protected function createWorkspaceListForSelectOptions()
244
    {
245
        if (!ExtensionManagementUtility::isLoaded('workspaces')) {
246
            return [];
247
        }
248
        $workspaceArray = [];
249
        // Two meta entries: 'all' and 'live'
250
        $workspaceArray[-99] = LocalizationUtility::translate('any', 'Belog');
251
        $workspaceArray[0] = LocalizationUtility::translate('live', 'Belog');
252
        $workspaces = $this->workspaceRepository->findAll();
253
        /** @var \TYPO3\CMS\Belog\Domain\Model\Workspace $workspace */
254
        foreach ($workspaces as $workspace) {
255
            $workspaceArray[$workspace->getUid()] = $workspace->getUid() . ': ' . $workspace->getTitle();
256
        }
257
        return $workspaceArray;
258
    }
259
260
    /**
261
     * If the user is in a workspace different than LIVE,
262
     * we force to show only log entries from the selected workspace,
263
     * and the workspace selector is not shown.
264
     *
265
     * @param Constraint $constraint
266
     */
267
    protected function forceWorkspaceSelectionIfInWorkspace(Constraint $constraint)
268
    {
269
        if (!ExtensionManagementUtility::isLoaded('workspaces')) {
270
            $this->view->assign('showWorkspaceSelector', false);
271
        } elseif ($GLOBALS['BE_USER']->workspace !== 0) {
272
            $constraint->setWorkspaceUid($GLOBALS['BE_USER']->workspace);
273
            $this->view->assign('showWorkspaceSelector', false);
274
        } else {
275
            $this->view->assign('showWorkspaceSelector', true);
276
        }
277
    }
278
279
    /**
280
     * Create options for the 'depth of page levels' selector.
281
     * This is shown if the module is displayed in page -> info
282
     *
283
     * @return array Key is depth identifier (1 = One level), value the localized select option label
284
     */
285
    protected function createPageDepthOptions()
286
    {
287
        $options = [
288
            0 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0', 'lang'),
289
            1 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1', 'lang'),
290
            2 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2', 'lang'),
291
            3 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3', 'lang'),
292
            4 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4', 'lang'),
293
            999 => LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi', 'lang')
294
        ];
295
        return $options;
296
    }
297
298
    /**
299
     * Calculate the start- and end timestamp
300
     *
301
     * @param Constraint $constraint
302
     */
303
    protected function setStartAndEndTimeFromTimeSelector(Constraint $constraint)
304
    {
305
        $startTime = $constraint->getManualDateStart() ? $constraint->getManualDateStart()->getTimestamp() : 0;
306
        $endTime = $constraint->getManualDateStop() ? $constraint->getManualDateStop()->getTimestamp() : 0;
307
        if ($endTime <= $startTime) {
308
            $endTime = $GLOBALS['EXEC_TIME'];
309
        }
310
        $constraint->setStartTimestamp($startTime);
311
        $constraint->setEndTimestamp($endTime);
312
    }
313
}
314