Issues (138)

Classes/Backend/RequestForm/LogRequestForm.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Backend\RequestForm;
6
7
use AOE\Crawler\Backend\Helper\ResultHandler;
8
use AOE\Crawler\Backend\Helper\UrlBuilder;
9
use AOE\Crawler\Converter\JsonCompatibilityConverter;
10
use AOE\Crawler\Domain\Repository\QueueRepository;
11
use AOE\Crawler\Utility\MessageUtility;
12
use AOE\Crawler\Value\QueueFilter;
13
use AOE\Crawler\Writer\FileWriter\CsvWriter\CrawlerCsvWriter;
14
use AOE\Crawler\Writer\FileWriter\CsvWriter\CsvWriterInterface;
15
use Doctrine\DBAL\Query\QueryBuilder;
16
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
17
use TYPO3\CMS\Backend\Utility\BackendUtility;
18
use TYPO3\CMS\Core\Database\ConnectionPool;
19
use TYPO3\CMS\Core\Imaging\Icon;
20
use TYPO3\CMS\Core\Imaging\IconFactory;
21
use TYPO3\CMS\Core\Utility\DebugUtility;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Extbase\Object\ObjectManager;
24
use TYPO3\CMS\Fluid\View\StandaloneView;
25
use TYPO3\CMS\Info\Controller\InfoModuleController;
26
27
final class LogRequestForm extends AbstractRequestForm implements RequestFormInterface
28
{
29
    /**
30
     * @var StandaloneView
31
     */
32
    private $view;
33
34
    /**
35
     * @var JsonCompatibilityConverter
36
     */
37
    private $jsonCompatibilityConverter;
38
39
    /**
40
     * @var int
41
     */
42
    private $pageId;
43
44
    /**
45
     * @var bool
46
     */
47
    private $CSVExport = false;
48
49
    /**
50
     * @var InfoModuleController
51
     */
52
    private $infoModuleController;
53
54
    /**
55
     * @var QueryBuilder
56
     */
57
    private $queryBuilder;
58
59
    /**
60
     * @var CsvWriterInterface
61
     */
62
    private $csvWriter;
63
64
    /**
65
     * @var QueueRepository
66
     */
67
    private $queueRepository;
68
69
    /**
70
     * @var array
71
     */
72
    private $CSVaccu = [];
73
74 1
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController, array $extensionSettings)
75
    {
76 1
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
77 1
        $this->view = $view;
78 1
        $this->infoModuleController = $infoModuleController;
79 1
        $this->jsonCompatibilityConverter = new JsonCompatibilityConverter();
80 1
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(QueueRepository::TABLE_NAME);
81 1
        $this->csvWriter = new CrawlerCsvWriter();
82 1
        $this->extensionSettings = $extensionSettings;
83 1
        $this->queueRepository = $objectManager->get(QueueRepository::class);
84 1
    }
85
86
    public function render($id, string $elementName, array $menuItems): string
87
    {
88
        $quiPart = GeneralUtility::_GP('qid_details') ? '&qid_details=' . (int) GeneralUtility::_GP('qid_details') : '';
89
        $setId = (int) GeneralUtility::_GP('setID');
90
        $this->pageId = $id;
91
92
        return $this->getDepthDropDownHtml($id, $elementName, $menuItems)
93
            . $this->showLogAction($setId, $quiPart);
94
    }
95
96
    private function getDepthDropDownHtml($id, string $currentValue, array $menuItems): string
97
    {
98
        return BackendUtility::getFuncMenu(
99
            $id,
100
            'SET[depth]',
101
            $currentValue,
102
            $menuItems
103
        );
104
    }
105
106
    /*******************************
107
     *
108
     * Shows log of indexed URLs
109
     *
110
     ******************************/
111
112
    /**
113
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
114
     * @throws \TYPO3\CMS\Extbase\Object\Exception
115
     */
116
    private function showLogAction(int $setId, string $quiPath): string
117
    {
118
        $this->view->setTemplate('ShowLog');
119
        if (empty($this->pageId)) {
120
            $this->isErrorDetected = true;
121
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
122
        } else {
123
            $this->findCrawler()->setID = GeneralUtility::md5int(microtime());
124
125
            $csvExport = GeneralUtility::_POST('_csv');
126
            $this->CSVExport = isset($csvExport);
127
128
            // Read URL:
129
            if (GeneralUtility::_GP('qid_read')) {
130
                $this->findCrawler()->readUrl((int) GeneralUtility::_GP('qid_read'), true);
131
            }
132
133
            // Look for set ID sent - if it is, we will display contents of that set:
134
            $showSetId = (int) GeneralUtility::_GP('setID');
135
136
            $queueId = GeneralUtility::_GP('qid_details');
137
            $this->view->assign('queueId', $queueId);
138
            $this->view->assign('setId', $showSetId);
139
            // Show details:
140
            if ($queueId) {
141
                // Get entry record:
142
                $q_entry = $this->queryBuilder
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\ForwardCompatibility\Result::fetch() has been deprecated: Use fetchNumeric(), fetchAssociative() or fetchOne() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

142
                $q_entry = /** @scrutinizer ignore-deprecated */ $this->queryBuilder

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
143
                    ->from(QueueRepository::TABLE_NAME)
144
                    ->select('*')
145
                    ->where(
146
                        $this->queryBuilder->expr()->eq('qid', $this->queryBuilder->createNamedParameter($queueId))
147
                    )
148
                    ->execute()
149
                    ->fetch();
150
151
                // Explode values
152
                $q_entry['parameters'] = $this->jsonCompatibilityConverter->convert($q_entry['parameters']);
153
                $q_entry['result_data'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']);
154
                $resStatus = ResultHandler::getResStatus($q_entry['result_data']);
155
                if (is_array($q_entry['result_data'])) {
156
                    $q_entry['result_data']['content'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']['content']);
157
                    if (! $this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
158
                        if (is_array($q_entry['result_data']['content'])) {
159
                            unset($q_entry['result_data']['content']['log']);
160
                        }
161
                    }
162
                }
163
164
                $this->view->assign('queueStatus', $resStatus);
165
                $this->view->assign('queueDetails', DebugUtility::viewArray($q_entry));
166
            } else {
167
                // Show list
168
                // Drawing tree:
169
                $tree = GeneralUtility::makeInstance(PageTreeView::class);
170
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
171
                $tree->init('AND ' . $perms_clause);
172
173
                // Set root row:
174
                $pageinfo = BackendUtility::readPageAccess(
175
                    $this->pageId,
176
                    $perms_clause
177
                );
178
                $HTML = $this->getIconFactory()->getIconForRecord('pages', $pageinfo, Icon::SIZE_SMALL)->render();
0 ignored issues
show
It seems like $pageinfo can also be of type false; however, parameter $row of TYPO3\CMS\Core\Imaging\I...ory::getIconForRecord() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
                $HTML = $this->getIconFactory()->getIconForRecord('pages', /** @scrutinizer ignore-type */ $pageinfo, Icon::SIZE_SMALL)->render();
Loading history...
179
                $tree->tree[] = [
180
                    'row' => $pageinfo,
181
                    'HTML' => $HTML,
182
                ];
183
184
                // Get branch beneath:
185
                if ($this->infoModuleController->MOD_SETTINGS['depth']) {
186
                    $tree->getTree($this->pageId, $this->infoModuleController->MOD_SETTINGS['depth']);
187
                }
188
189
                // If Flush button is pressed, flush tables instead of selecting entries:
190
                if (GeneralUtility::_POST('_flush')) {
191
                    $doFlush = true;
192
                } elseif (GeneralUtility::_POST('_flush_all')) {
193
                    $doFlush = true;
194
                    $this->infoModuleController->MOD_SETTINGS['log_display'] = 'all';
195
                } else {
196
                    $doFlush = false;
197
                }
198
                $itemsPerPage = (int) $this->infoModuleController->MOD_SETTINGS['itemsPerPage'];
199
                $queueFilter = new QueueFilter($this->infoModuleController->MOD_SETTINGS['log_display']);
200
201
                if ($doFlush) {
202
                    $this->queueRepository->flushQueue($queueFilter);
203
                }
204
205
                // Traverse page tree:
206
                $count = 0;
207
                $logEntriesPerPage = [];
208
                foreach ($tree->tree as $data) {
209
                    $logEntriesOfPage = $this->queueRepository->getQueueEntriesForPageId(
210
                        (int) $data['row']['uid'],
211
                        $itemsPerPage,
212
                        $queueFilter
213
                    );
214
215
                    $logEntriesPerPage[] = $this->drawLog_addRows(
216
                        $logEntriesOfPage,
217
                        $data['HTML'] . BackendUtility::getRecordTitle('pages', $data['row'], true)
218
                    );
219
                    if (++$count === 1000) {
220
                        break;
221
                    }
222
                }
223
224
                $this->view->assign('logEntriesPerPage', $logEntriesPerPage);
225
            }
226
227
            if ($this->CSVExport) {
228
                $this->outputCsvFile();
229
            }
230
        }
231
        $this->view->assign('showResultLog', (bool) $this->infoModuleController->MOD_SETTINGS['log_resultLog']);
232
        $this->view->assign('showFeVars', (bool) $this->infoModuleController->MOD_SETTINGS['log_feVars']);
233
        $this->view->assign('displayActions', 1);
234
        $this->view->assign('displayLogFilterHtml', $this->getDisplayLogFilterHtml($setId));
235
        $this->view->assign('itemPerPageHtml', $this->getItemsPerPageDropDownHtml());
236
        $this->view->assign('showResultLogHtml', $this->getShowResultLogCheckBoxHtml($setId, $quiPath));
237
        $this->view->assign('showFeVarsHtml', $this->getShowFeVarsCheckBoxHtml($setId, $quiPath));
238
        return $this->view->render();
239
    }
240
241
    /**
242
     * Outputs the CSV file and sets the correct headers
243
     */
244
    private function outputCsvFile(): void
245
    {
246
        if (! count($this->CSVaccu)) {
247
            MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.canNotExportEmptyQueueToCsvText'));
248
            return;
249
        }
250
251
        $csvString = $this->csvWriter->arrayToCsv($this->CSVaccu);
252
253
        header('Content-Type: application/octet-stream');
254
        header('Content-Disposition: attachment; filename=CrawlerLog.csv');
255
        echo $csvString;
256
257
        exit;
258
    }
259
260
    private function getIconFactory(): IconFactory
261
    {
262
        return GeneralUtility::makeInstance(IconFactory::class);
263
    }
264
265
    private function getDisplayLogFilterHtml(int $setId): string
266
    {
267
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.display') . ': ' . BackendUtility::getFuncMenu(
268
                $this->pageId,
269
                'SET[log_display]',
270
                $this->infoModuleController->MOD_SETTINGS['log_display'],
271
                $this->infoModuleController->MOD_MENU['log_display'],
272
                'index.php',
273
                '&setID=' . $setId
274
            );
275
    }
276
277
    private function getItemsPerPageDropDownHtml(): string
278
    {
279
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage') . ': ' .
280
            BackendUtility::getFuncMenu(
281
                $this->pageId,
282
                'SET[itemsPerPage]',
283
                $this->infoModuleController->MOD_SETTINGS['itemsPerPage'],
284
                $this->infoModuleController->MOD_MENU['itemsPerPage']
285
            );
286
    }
287
288
    private function getShowResultLogCheckBoxHtml(int $setId, string $quiPart): string
289
    {
290
        return BackendUtility::getFuncCheck(
291
                $this->pageId,
292
                'SET[log_resultLog]',
293
                $this->infoModuleController->MOD_SETTINGS['log_resultLog'],
294
                'index.php',
295
                '&setID=' . $setId . $quiPart
296
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showresultlog');
297
    }
298
299
    private function getShowFeVarsCheckBoxHtml(int $setId, string $quiPart): string
300
    {
301
        return BackendUtility::getFuncCheck(
302
                $this->pageId,
303
                'SET[log_feVars]',
304
                $this->infoModuleController->MOD_SETTINGS['log_feVars'],
305
                'index.php',
306
                '&setID=' . $setId . $quiPart
307
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showfevars');
308
    }
309
310
    /**
311
     * Create the rows for display of the page tree
312
     * For each page a number of rows are shown displaying GET variable configuration
313
     *
314
     * @param array $logEntriesOfPage Log items of one page
315
     * @param string $titleString Title string
316
     *
317
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
318
     *
319
     * @psalm-return non-empty-list<array{titleRowSpan: positive-int, colSpan: int, title: string, noEntries?: string, trClass?: string, qid?: array{link: \TYPO3\CMS\Core\Http\Uri, link-text: string}, refresh?: array{link: \TYPO3\CMS\Core\Http\Uri, link-text: Icon, warning: Icon|string}, columns?: array{url: mixed|string, scheduled: string, exec_time: string, result_log: string, result_status: string, feUserGroupList: string, procInstructions: string, set_id: string, tsfe_id: string, tsfe_gr_list: string}}>
320
     */
321
    private function drawLog_addRows(array $logEntriesOfPage, string $titleString): array
322
    {
323
        $resultArray = [];
324
        $contentArray = [];
325
326
        $contentArray['titleRowSpan'] = 1;
327
        $contentArray['colSpan'] = 9
328
            + ($this->infoModuleController->MOD_SETTINGS['log_resultLog'] ? -1 : 0)
329
            + ($this->infoModuleController->MOD_SETTINGS['log_feVars'] ? 3 : 0);
330
331
        if (! empty($logEntriesOfPage)) {
332
            $setId = (int) GeneralUtility::_GP('setID');
333
            $refreshIcon = $this->getIconFactory()->getIcon('actions-system-refresh', Icon::SIZE_SMALL);
334
            // Traverse parameter combinations:
335
            $firstIteration = true;
336
            foreach ($logEntriesOfPage as $vv) {
337
                // Title column:
338
                if ($firstIteration) {
339
                    $contentArray['titleRowSpan'] = count($logEntriesOfPage);
340
                    $contentArray['title'] = $titleString;
341
                } else {
342
                    $contentArray['title'] = '';
343
                    $contentArray['titleRowSpan'] = 1;
344
                }
345
346
                $firstIteration = false;
347
                $execTime = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
348
349
                // Result:
350
                $resLog = ResultHandler::getResultLog($vv);
351
352
                $resultData = $vv['result_data'] ? $this->jsonCompatibilityConverter->convert($vv['result_data']) : [];
353
                $resStatus = ResultHandler::getResStatus($resultData);
354
355
                // Compile row:
356
                $parameters = $this->jsonCompatibilityConverter->convert($vv['parameters']);
357
358
                // Put data into array:
359
                $rowData = [];
360
                if ($this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
361
                    $rowData['result_log'] = $resLog;
362
                } else {
363
                    $rowData['scheduled'] = ($vv['scheduled'] > 0) ? BackendUtility::datetime($vv['scheduled']) : 0;
364
                    $rowData['exec_time'] = $execTime;
365
                }
366
                $rowData['result_status'] = GeneralUtility::fixed_lgd_cs($resStatus, 50);
367
                $url = htmlspecialchars($parameters['url'] ?? $parameters['alturl'], ENT_QUOTES | ENT_HTML5);
368
                $rowData['url'] = '<a href="' . $url . '" target="_newWIndow">' . $url . '</a>';
369
                $rowData['feUserGroupList'] = $parameters['feUserGroupList'] ?: '';
370
                $rowData['procInstructions'] = is_array($parameters['procInstructions']) ? implode('; ', $parameters['procInstructions']) : '';
371
                $rowData['set_id'] = (string) $vv['set_id'];
372
373
                if ($this->infoModuleController->MOD_SETTINGS['log_feVars']) {
374
                    $resFeVars = ResultHandler::getResFeVars($resultData ?: []);
0 ignored issues
show
It seems like $resultData ?: array() can also be of type true; however, parameter $resultData of AOE\Crawler\Backend\Help...Handler::getResFeVars() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

374
                    $resFeVars = ResultHandler::getResFeVars(/** @scrutinizer ignore-type */ $resultData ?: []);
Loading history...
375
                    $rowData['tsfe_id'] = $resFeVars['id'] ?: '';
376
                    $rowData['tsfe_gr_list'] = $resFeVars['gr_list'] ?: '';
377
                }
378
379
                $trClass = '';
380
                $warningIcon = '';
381
                if (str_contains($resStatus, 'Error:')) {
382
                    $trClass = 'bg-danger';
383
                    $warningIcon = $this->getIconFactory()->getIcon('actions-ban', Icon::SIZE_SMALL);
384
                }
385
386
                // Put rows together:
387
                $contentArray['trClass'] = $trClass;
388
                $contentArray['qid'] = [
389
                    'link' => UrlBuilder::getInfoModuleUrl(['qid_details' => $vv['qid'], 'setID' => $setId]),
390
                    'link-text' => htmlspecialchars((string) $vv['qid'], ENT_QUOTES | ENT_HTML5),
391
                ];
392
                $contentArray['refresh'] = [
393
                    'link' => UrlBuilder::getInfoModuleUrl(['qid_read' => $vv['qid'], 'setID' => $setId]),
394
                    'link-text' => $refreshIcon,
395
                    'warning' => $warningIcon,
396
                ];
397
398
                foreach ($rowData as $fKey => $value) {
399
                    if ($fKey === 'url') {
400
                        $contentArray['columns'][$fKey] = $value;
401
                    } else {
402
                        $contentArray['columns'][$fKey] = nl2br(htmlspecialchars((string) $value, ENT_QUOTES | ENT_HTML5));
403
                    }
404
                }
405
406
                $resultArray[] = $contentArray;
407
408
                if ($this->CSVExport) {
409
                    // Only for CSV (adding qid and scheduled/exec_time if needed):
410
                    $csvExport['scheduled'] = BackendUtility::datetime($vv['scheduled']);
411
                    $csvExport['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
412
                    $csvExport['result_status'] = $contentArray['columns']['result_status'];
413
                    $csvExport['url'] = $contentArray['columns']['url'];
414
                    $csvExport['feUserGroupList'] = $contentArray['columns']['feUserGroupList'];
415
                    $csvExport['procInstructions'] = $contentArray['columns']['procInstructions'];
416
                    $csvExport['set_id'] = $contentArray['columns']['set_id'];
417
                    $csvExport['result_log'] = str_replace(chr(10), '// ', $resLog);
418
                    $csvExport['qid'] = $vv['qid'];
419
                    $this->CSVaccu[] = $csvExport;
420
                }
421
            }
422
        } else {
423
            $contentArray['title'] = $titleString;
424
            $contentArray['noEntries'] = $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noentries');
425
426
            $resultArray[] = $contentArray;
427
        }
428
429
        return $resultArray;
430
    }
431
}
432