• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tomasnorre / crawler / 13046756300

30 Jan 2025 06:18AM UTC coverage: 75.173%. First build
13046756300

Pull #1121

github

web-flow
Merge ce71f3527 into 2ad932550
Pull Request #1121: [BUGFIX] Fix showing "Scheduled" and "Run-time" columns in backend log

0 of 1 new or added line in 1 file covered. (0.0%)

1953 of 2598 relevant lines covered (75.17%)

3.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.44
/Classes/Backend/RequestForm/LogRequestForm.php
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
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController, array $extensionSettings)
75
    {
76
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
2✔
77
        $this->view = $view;
2✔
78
        $this->infoModuleController = $infoModuleController;
2✔
79
        $this->jsonCompatibilityConverter = new JsonCompatibilityConverter();
2✔
80
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(QueueRepository::TABLE_NAME);
2✔
81
        $this->csvWriter = new CrawlerCsvWriter();
2✔
82
        $this->extensionSettings = $extensionSettings;
2✔
83
        $this->queueRepository = $objectManager->get(QueueRepository::class);
2✔
84
    }
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') : '';
1✔
89
        $setId = (int) GeneralUtility::_GP('setID');
1✔
90
        $this->pageId = $id;
1✔
91

92
        return $this->getDepthDropDownHtml($id, $elementName, $menuItems)
1✔
93
            . $this->showLogAction($setId, $quiPart);
1✔
94
    }
95

96
    private function getDepthDropDownHtml($id, string $currentValue, array $menuItems): string
97
    {
98
        return BackendUtility::getFuncMenu(
1✔
99
            $id,
1✔
100
            'SET[depth]',
1✔
101
            $currentValue,
1✔
102
            $menuItems
1✔
103
        );
1✔
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');
1✔
119
        if (empty($this->pageId)) {
1✔
120
            $this->isErrorDetected = true;
1✔
121
            $this->view->assign('noPageSelected', true);
1✔
122
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
1✔
123
        } else {
124
            $this->findCrawler()->setID = GeneralUtility::md5int(microtime());
×
125

126
            $csvExport = GeneralUtility::_POST('_csv');
×
127
            $this->CSVExport = isset($csvExport);
×
128

129
            // Read URL:
130
            if (GeneralUtility::_GP('qid_read')) {
×
131
                $this->findCrawler()->readUrl((int) GeneralUtility::_GP('qid_read'), true);
×
132
            }
133

134
            // Look for set ID sent - if it is, we will display contents of that set:
135
            $showSetId = (int) GeneralUtility::_GP('setID');
×
136

137
            $queueId = GeneralUtility::_GP('qid_details');
×
138
            $this->view->assign('queueId', $queueId);
×
139
            $this->view->assign('setId', $showSetId);
×
140
            $this->view->assign('noPageSelected', false);
×
141
            // Show details:
142
            if ($queueId) {
×
143
                // Get entry record:
144
                $q_entry = $this->queryBuilder
×
145
                    ->from(QueueRepository::TABLE_NAME)
×
146
                    ->select('*')
×
147
                    ->where(
×
148
                        $this->queryBuilder->expr()->eq('qid', $this->queryBuilder->createNamedParameter($queueId))
×
149
                    )
×
150
                    ->execute()
×
151
                    ->fetch();
×
152

153
                // Explode values
154
                $q_entry['parameters'] = $this->jsonCompatibilityConverter->convert($q_entry['parameters']);
×
155
                $q_entry['result_data'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']);
×
156
                $resStatus = ResultHandler::getResStatus($q_entry['result_data']);
×
157
                if (is_array($q_entry['result_data'])) {
×
158
                    $q_entry['result_data']['content'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']['content']);
×
159
                    if (isset($this->infoModuleController->MOD_SETTINGS['log_resultLog']) && ! $this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
×
160
                        if (is_array($q_entry['result_data']['content'])) {
×
161
                            unset($q_entry['result_data']['content']['log']);
×
162
                        }
163
                    }
164
                }
165

166
                $this->view->assign('queueStatus', $resStatus);
×
167
                $this->view->assign('queueDetails', DebugUtility::viewArray($q_entry));
×
168
            } else {
169
                // Show list
170
                // Drawing tree:
171
                $tree = GeneralUtility::makeInstance(PageTreeView::class);
×
172
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
×
173
                $tree->init('AND ' . $perms_clause);
×
174

175
                // Set root row:
176
                $pageinfo = BackendUtility::readPageAccess(
×
177
                    $this->pageId,
×
178
                    $perms_clause
×
179
                );
×
180

181
                $HTML = $this->getIconFactory()->getIconForRecord('pages', $pageinfo, Icon::SIZE_SMALL)->render();
×
182
                $tree->tree[] = [
×
183
                    'row' => $pageinfo,
×
184
                    'HTML' => $HTML,
×
185
                ];
×
186

187
                // Get branch beneath:
188
                if ($this->infoModuleController->MOD_SETTINGS['depth']) {
×
189
                    $tree->getTree($this->pageId, $this->infoModuleController->MOD_SETTINGS['depth']);
×
190
                }
191

192
                // If Flush button is pressed, flush tables instead of selecting entries:
193
                if (GeneralUtility::_POST('_flush')) {
×
194
                    $doFlush = true;
×
195
                } elseif (GeneralUtility::_POST('_flush_all')) {
×
196
                    $doFlush = true;
×
197
                    $this->infoModuleController->MOD_SETTINGS['log_display'] = 'all';
×
198
                } else {
199
                    $doFlush = false;
×
200
                }
201
                $itemsPerPage = (int) $this->infoModuleController->MOD_SETTINGS['itemsPerPage'];
×
202
                $queueFilter = new QueueFilter($this->infoModuleController->MOD_SETTINGS['log_display']);
×
203

204
                if ($doFlush) {
×
205
                    $this->queueRepository->flushQueue($queueFilter);
×
206
                }
207

208
                // Traverse page tree:
209
                $count = 0;
×
210
                $logEntriesPerPage = [];
×
211
                foreach ($tree->tree as $data) {
×
212
                    $logEntriesOfPage = $this->queueRepository->getQueueEntriesForPageId(
×
213
                        (int) $data['row']['uid'],
×
214
                        $itemsPerPage,
×
215
                        $queueFilter
×
216
                    );
×
217

218
                    $logEntriesPerPage[] = $this->drawLog_addRows(
×
219
                        $logEntriesOfPage,
×
220
                        $data['HTML'] . BackendUtility::getRecordTitle('pages', $data['row'], true)
×
221
                    );
×
222
                    if (++$count === 1000) {
×
223
                        break;
×
224
                    }
225
                }
226

227
                $this->view->assign('logEntriesPerPage', $logEntriesPerPage);
×
228
            }
229

230
            if ($this->CSVExport) {
×
231
                $this->outputCsvFile();
×
232
            }
233
        }
234
        $this->view->assign('showResultLog', (bool) isset($this->infoModuleController->MOD_SETTINGS['log_resultLog']) ? $this->infoModuleController->MOD_SETTINGS['log_resultLog'] : false);
1✔
235
        $this->view->assign('showFeVars', (bool) isset($this->infoModuleController->MOD_SETTINGS['log_feVars']) ? $this->infoModuleController->MOD_SETTINGS['log_feVars'] : false);
1✔
236
        $this->view->assign('displayActions', 1);
1✔
237
        $this->view->assign('displayLogFilterHtml', $this->getDisplayLogFilterHtml($setId));
1✔
238
        $this->view->assign('itemPerPageHtml', $this->getItemsPerPageDropDownHtml());
1✔
239
        $this->view->assign('showResultLogHtml', $this->getShowResultLogCheckBoxHtml($setId, $quiPath));
1✔
240
        $this->view->assign('showFeVarsHtml', $this->getShowFeVarsCheckBoxHtml($setId, $quiPath));
1✔
241
        return $this->view->render();
1✔
242
    }
243

244
    /**
245
     * Outputs the CSV file and sets the correct headers
246
     */
247
    private function outputCsvFile(): void
248
    {
249
        if (! count($this->CSVaccu)) {
×
250
            MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.canNotExportEmptyQueueToCsvText'));
×
251
            return;
×
252
        }
253

254
        $csvString = $this->csvWriter->arrayToCsv($this->CSVaccu);
×
255

256
        header('Content-Type: application/octet-stream');
×
257
        header('Content-Disposition: attachment; filename=CrawlerLog.csv');
×
258
        echo $csvString;
×
259

260
        exit;
×
261
    }
262

263
    private function getIconFactory(): IconFactory
264
    {
265
        return GeneralUtility::makeInstance(IconFactory::class);
×
266
    }
267

268
    private function getDisplayLogFilterHtml(int $setId): string
269
    {
270
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.display') . ': ' . BackendUtility::getFuncMenu(
1✔
271
                $this->pageId,
1✔
272
                'SET[log_display]',
1✔
273
                $this->infoModuleController->MOD_SETTINGS['log_display'],
1✔
274
                $this->infoModuleController->MOD_MENU['log_display'],
1✔
275
                'index.php',
1✔
276
                '&setID=' . $setId
1✔
277
            );
1✔
278
    }
279

280
    private function getItemsPerPageDropDownHtml(): string
281
    {
282
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage') . ': ' .
1✔
283
            BackendUtility::getFuncMenu(
1✔
284
                $this->pageId,
1✔
285
                'SET[itemsPerPage]',
1✔
286
                $this->infoModuleController->MOD_SETTINGS['itemsPerPage'],
1✔
287
                $this->infoModuleController->MOD_MENU['itemsPerPage']
1✔
288
            );
1✔
289
    }
290

291
    private function getShowResultLogCheckBoxHtml(int $setId, string $quiPart): string
292
    {
293
        $currentValue = $this->infoModuleController->MOD_SETTINGS['log_resultLog'] ?? '';
1✔
294

295
        return BackendUtility::getFuncCheck(
1✔
296
                $this->pageId,
1✔
297
                'SET[log_resultLog]',
1✔
298
                $currentValue,
1✔
299
                'index.php',
1✔
300
                '&setID=' . $setId . $quiPart
1✔
301
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showresultlog');
1✔
302
    }
303

304
    private function getShowFeVarsCheckBoxHtml(int $setId, string $quiPart): string
305
    {
306
        $currentValue = $this->infoModuleController->MOD_SETTINGS['log_feVars'] ?? '';
1✔
307
        return BackendUtility::getFuncCheck(
1✔
308
                $this->pageId,
1✔
309
                'SET[log_feVars]',
1✔
310
                $currentValue,
1✔
311
                'index.php',
1✔
312
                '&setID=' . $setId . $quiPart
1✔
313
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showfevars');
1✔
314
    }
315

316
    /**
317
     * Create the rows for display of the page tree
318
     * For each page a number of rows are shown displaying GET variable configuration
319
     *
320
     * @param array $logEntriesOfPage Log items of one page
321
     * @param string $titleString Title string
322
     *
323
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
324
     *
325
     * @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}}>
326
     */
327
    private function drawLog_addRows(array $logEntriesOfPage, string $titleString): array
328
    {
329
        $resultArray = [];
×
330
        $contentArray = [];
×
331

332
        $contentArray['titleRowSpan'] = 1;
×
333
        $contentArray['colSpan'] = 9
×
334
            + (isset($this->infoModuleController->MOD_SETTINGS['log_resultLog']) ? -1 : 0)
×
335
            + (isset($this->infoModuleController->MOD_SETTINGS['log_feVars']) ? 3 : 0);
×
336

337
        if (! empty($logEntriesOfPage)) {
×
338
            $setId = (int) GeneralUtility::_GP('setID');
×
339
            $refreshIcon = $this->getIconFactory()->getIcon('actions-system-refresh', Icon::SIZE_SMALL);
×
340
            // Traverse parameter combinations:
341
            $firstIteration = true;
×
342
            foreach ($logEntriesOfPage as $vv) {
×
343
                // Title column:
344
                if ($firstIteration) {
×
345
                    $contentArray['titleRowSpan'] = count($logEntriesOfPage);
×
346
                    $contentArray['title'] = $titleString;
×
347
                } else {
348
                    $contentArray['title'] = '';
×
349
                    $contentArray['titleRowSpan'] = 1;
×
350
                }
351

352
                $firstIteration = false;
×
353
                $execTime = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
×
354

355
                // Result:
356
                $resLog = ResultHandler::getResultLog($vv);
×
357

358
                $resultData = $vv['result_data'] ? $this->jsonCompatibilityConverter->convert($vv['result_data']) : [];
×
359
                $resStatus = ResultHandler::getResStatus($resultData);
×
360

361
                // Compile row:
362
                $parameters = $this->jsonCompatibilityConverter->convert($vv['parameters']);
×
363

364
                // Put data into array:
365
                $rowData = [];
×
NEW
366
                if (isset($this->infoModuleController->MOD_SETTINGS['log_resultLog']) && $this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
×
367
                    $rowData['result_log'] = $resLog;
×
368
                } else {
369
                    $rowData['scheduled'] = ($vv['scheduled'] > 0) ? BackendUtility::datetime($vv['scheduled']) : '-';
×
370
                    $rowData['exec_time'] = $execTime;
×
371
                }
372
                $rowData['result_status'] = GeneralUtility::fixed_lgd_cs($resStatus, 50);
×
373
                $url = htmlspecialchars((string) ($parameters['url'] ?? $parameters['alturl']), ENT_QUOTES | ENT_HTML5);
×
374
                $rowData['url'] = '<a href="' . $url . '" target="_newWIndow">' . $url . '</a>';
×
375
                $rowData['feUserGroupList'] = $parameters['feUserGroupList'] ?? '';
×
376
                $rowData['procInstructions'] = is_array($parameters['procInstructions']) ? implode('; ', $parameters['procInstructions']) : '';
×
377
                $rowData['set_id'] = (string) $vv['set_id'];
×
378

379
                if (isset($this->infoModuleController->MOD_SETTINGS['log_feVars'])) {
×
380
                    $resFeVars = ResultHandler::getResFeVars($resultData ?: []);
×
381
                    $rowData['tsfe_id'] = $resFeVars['id'] ?? '';
×
382
                    $rowData['tsfe_gr_list'] = $resFeVars['gr_list'] ?? '';
×
383
                }
384

385
                $trClass = '';
×
386
                $warningIcon = '';
×
387
                if (str_contains($resStatus, 'Error:')) {
×
388
                    $trClass = 'bg-danger';
×
389
                    $warningIcon = $this->getIconFactory()->getIcon('actions-ban', Icon::SIZE_SMALL);
×
390
                }
391

392
                // Put rows together:
393
                $contentArray['trClass'] = $trClass;
×
394
                $contentArray['qid'] = [
×
395
                    'link' => UrlBuilder::getInfoModuleUrl(['qid_details' => $vv['qid'], 'setID' => $setId]),
×
396
                    'link-text' => htmlspecialchars((string) $vv['qid'], ENT_QUOTES | ENT_HTML5),
×
397
                ];
×
398
                $contentArray['refresh'] = [
×
399
                    'link' => UrlBuilder::getInfoModuleUrl(['qid_read' => $vv['qid'], 'setID' => $setId]),
×
400
                    'link-text' => $refreshIcon,
×
401
                    'warning' => $warningIcon,
×
402
                ];
×
403

404
                foreach ($rowData as $fKey => $value) {
×
405
                    if ($fKey === 'url') {
×
406
                        $contentArray['columns'][$fKey] = $value;
×
407
                    } else {
408
                        $contentArray['columns'][$fKey] = nl2br(htmlspecialchars((string) $value, ENT_QUOTES | ENT_HTML5));
×
409
                    }
410
                }
411

412
                $resultArray[] = $contentArray;
×
413

414
                if ($this->CSVExport) {
×
415
                    // Only for CSV (adding qid and scheduled/exec_time if needed):
416
                    $csvExport['scheduled'] = BackendUtility::datetime($vv['scheduled']);
×
417
                    $csvExport['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
×
418
                    $csvExport['result_status'] = $contentArray['columns']['result_status'];
×
419
                    $csvExport['url'] = $contentArray['columns']['url'];
×
420
                    $csvExport['feUserGroupList'] = $contentArray['columns']['feUserGroupList'];
×
421
                    $csvExport['procInstructions'] = $contentArray['columns']['procInstructions'];
×
422
                    $csvExport['set_id'] = $contentArray['columns']['set_id'];
×
423
                    $csvExport['result_log'] = str_replace(chr(10), '// ', $resLog);
×
424
                    $csvExport['qid'] = $vv['qid'];
×
425
                    $this->CSVaccu[] = $csvExport;
×
426
                }
427
            }
428
        } else {
429
            $contentArray['title'] = $titleString;
×
430
            $contentArray['noEntries'] = $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noentries');
×
431

432
            $resultArray[] = $contentArray;
×
433
        }
434

435
        return $resultArray;
×
436
    }
437
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc