Passed
Push — refactor/backendModule-ValueOb... ( ba8b37...91b878 )
by Tomas Norre
07:37
created

LogRequestForm::getLanguageService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Backend\RequestForm;
6
7
use AOE\Crawler\Backend\Helper\UrlBuilder;
8
use AOE\Crawler\Converter\JsonCompatibilityConverter;
9
use AOE\Crawler\Utility\MessageUtility;
10
use AOE\Crawler\Writer\FileWriter\CsvWriter\CrawlerCsvWriter;
11
use AOE\Crawler\Writer\FileWriter\CsvWriter\CsvWriterInterface;
12
use Doctrine\DBAL\Query\QueryBuilder;
13
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
14
use TYPO3\CMS\Backend\Utility\BackendUtility;
15
use TYPO3\CMS\Core\Database\ConnectionPool;
16
use TYPO3\CMS\Core\Imaging\Icon;
17
use TYPO3\CMS\Core\Imaging\IconFactory;
18
use TYPO3\CMS\Core\Utility\DebugUtility;
19
use TYPO3\CMS\Core\Utility\GeneralUtility;
20
use TYPO3\CMS\Fluid\View\StandaloneView;
21
use TYPO3\CMS\Info\Controller\InfoModuleController;
22
23
final class LogRequestForm extends AbstractRequestForm implements RequestFormInterface
24
{
25
    /**
26
     * @var StandaloneView
27
     */
28
    private $view;
29
30
    /**
31
     * @var JsonCompatibilityConverter
32
     */
33
    private $jsonCompatibilityConverter;
34
35
    /**
36
     * @var int
37
     */
38
    private $pageId;
39
40
    /**
41
     * @var bool
42
     */
43
    private $CSVExport = false;
44
45
    /**
46
     * @var InfoModuleController
47
     */
48
    private $infoModuleController;
49
50
    /**
51
     * @var QueryBuilder
52
     */
53
    private $queryBuilder;
54
55
    /**
56
     * @var CsvWriterInterface
57
     */
58
    private $csvWriter;
59
60
    /**
61
     * @var array
62
     */
63
    private $CSVaccu = [];
64
65
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController, array $extensionSettings)
66
    {
67
        $this->view = $view;
68
        $this->infoModuleController = $infoModuleController;
69
        $this->jsonCompatibilityConverter = new JsonCompatibilityConverter();
70
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_crawler_queue');
71
        $this->csvWriter = new CrawlerCsvWriter();
72
        $this->extensionSettings = $extensionSettings;
73
    }
74
75
    public function render($id, string $currentValue, array $menuItems): string
76
    {
77
        $quiPart = GeneralUtility::_GP('qid_details') ? '&qid_details=' . (int) GeneralUtility::_GP('qid_details') : '';
78
        $setId = (int) GeneralUtility::_GP('setID');
79
        $this->pageId = $id;
80
81
        return $this->getDepthDropDownHtml($id, $currentValue, $menuItems)
82
            . $this->showLogAction($setId, $quiPart);
83
    }
84
85
    private function getDepthDropDownHtml($id, string $currentValue, array $menuItems): string
86
    {
87
        return BackendUtility::getFuncMenu(
88
            $id,
89
            'SET[depth]',
90
            $currentValue,
91
            $menuItems
92
        );
93
    }
94
95
    /*******************************
96
     *
97
     * Shows log of indexed URLs
98
     *
99
     ******************************/
100
101
    /**
102
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
103
     */
104
    private function showLogAction(int $setId, string $quiPath): string
105
    {
106
        $this->view->setTemplate('ShowLog');
107
        if (empty($this->pageId)) {
108
            $this->isErrorDetected = true;
109
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
110
        } else {
111
            $this->findCrawler()->setAccessMode('gui');
112
            $this->findCrawler()->setID = GeneralUtility::md5int(microtime());
113
114
            $csvExport = GeneralUtility::_POST('_csv');
115
            $this->CSVExport = isset($csvExport);
116
117
            // Read URL:
118
            if (GeneralUtility::_GP('qid_read')) {
119
                $this->findCrawler()->readUrl((int) GeneralUtility::_GP('qid_read'), true);
120
            }
121
122
            // Look for set ID sent - if it is, we will display contents of that set:
123
            $showSetId = (int) GeneralUtility::_GP('setID');
124
125
            $queueId = GeneralUtility::_GP('qid_details');
126
            $this->view->assign('queueId', $queueId);
127
            $this->view->assign('setId', $showSetId);
128
            // Show details:
129
            if ($queueId) {
130
                // Get entry record:
131
                $q_entry = $this->queryBuilder
132
                    ->from('tx_crawler_queue')
133
                    ->select('*')
134
                    ->where(
135
                        $this->queryBuilder->expr()->eq('qid', $this->queryBuilder->createNamedParameter($queueId))
136
                    )
137
                    ->execute()
138
                    ->fetch();
139
140
                // Explode values
141
                $q_entry['parameters'] = $this->jsonCompatibilityConverter->convert($q_entry['parameters']);
142
                $q_entry['result_data'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']);
143
                $resStatus = $this->getResStatus($q_entry['result_data']);
144
                if (is_array($q_entry['result_data'])) {
145
                    $q_entry['result_data']['content'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']['content']);
146
                    if (! $this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
147
                        unset($q_entry['result_data']['content']['log']);
148
                    }
149
                }
150
151
                $this->view->assign('queueStatus', $resStatus);
152
                $this->view->assign('queueDetails', DebugUtility::viewArray($q_entry));
153
            } else {
154
                // Show list
155
                // Drawing tree:
156
                $tree = GeneralUtility::makeInstance(PageTreeView::class);
157
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
158
                $tree->init('AND ' . $perms_clause);
159
160
                // Set root row:
161
                $pageinfo = BackendUtility::readPageAccess(
162
                    $this->pageId,
163
                    $perms_clause
164
                );
165
                $HTML = $this->getIconFactory()->getIconForRecord('pages', $pageinfo, Icon::SIZE_SMALL)->render();
166
                $tree->tree[] = [
167
                    'row' => $pageinfo,
168
                    'HTML' => $HTML,
169
                ];
170
171
                // Get branch beneath:
172
                if ($this->infoModuleController->MOD_SETTINGS['depth']) {
173
                    $tree->getTree($this->pageId, $this->infoModuleController->MOD_SETTINGS['depth']);
174
                }
175
176
                // If Flush button is pressed, flush tables instead of selecting entries:
177
                if (GeneralUtility::_POST('_flush')) {
178
                    $doFlush = true;
179
                    $doFullFlush = false;
180
                } elseif (GeneralUtility::_POST('_flush_all')) {
181
                    $doFlush = true;
182
                    $doFullFlush = true;
183
                } else {
184
                    $doFlush = false;
185
                    $doFullFlush = false;
186
                }
187
                $itemsPerPage = (int) $this->infoModuleController->MOD_SETTINGS['itemsPerPage'];
188
                // Traverse page tree:
189
                $code = '';
190
                $count = 0;
191
                foreach ($tree->tree as $data) {
192
                    // Get result:
193
                    $logEntriesOfPage = $this->crawlerController->getLogEntriesForPageId(
194
                        (int) $data['row']['uid'],
195
                        $this->infoModuleController->MOD_SETTINGS['log_display'],
196
                        $doFlush,
197
                        $doFullFlush,
198
                        $itemsPerPage
199
                    );
200
201
                    $code .= $this->drawLog_addRows(
202
                        $logEntriesOfPage,
203
                        $data['HTML'] . BackendUtility::getRecordTitle('pages', $data['row'], true)
204
                    );
205
                    if (++$count === 1000) {
206
                        break;
207
                    }
208
                }
209
                $this->view->assign('code', $code);
210
            }
211
212
            if ($this->CSVExport) {
213
                $this->outputCsvFile();
214
            }
215
        }
216
        $this->view->assign('showResultLog', (bool) $this->infoModuleController->MOD_SETTINGS['log_resultLog']);
217
        $this->view->assign('showFeVars', (bool) $this->infoModuleController->MOD_SETTINGS['log_feVars']);
218
        $this->view->assign('displayActions', 1);
219
        $this->view->assign('displayLogFilterHtml', $this->getDisplayLogFilterHtml($setId));
220
        $this->view->assign('itemPerPageHtml', $this->getItemsPerPageDropDownHtml());
221
        $this->view->assign('showResultLogHtml', $this->getShowResultLogCheckBoxHtml($setId, $quiPath));
222
        $this->view->assign('showFeVarsHtml', $this->getShowFeVarsCheckBoxHtml($setId, $quiPath));
223
        return $this->view->render();
224
    }
225
226
    /**
227
     * Outputs the CSV file and sets the correct headers
228
     */
229
    private function outputCsvFile(): void
230
    {
231
        if (! count($this->CSVaccu)) {
232
            MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.canNotExportEmptyQueueToCsvText'));
233
            return;
234
        }
235
236
        $csvString = $this->csvWriter->arrayToCsv($this->CSVaccu);
237
238
        header('Content-Type: application/octet-stream');
239
        header('Content-Disposition: attachment; filename=CrawlerLog.csv');
240
        echo $csvString;
241
242
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
243
    }
244
245
    private function getIconFactory(): IconFactory
246
    {
247
        return GeneralUtility::makeInstance(IconFactory::class);
248
    }
249
250
    private function getDisplayLogFilterHtml(int $setId): string
251
    {
252
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.display') . ': ' . BackendUtility::getFuncMenu(
253
                $this->pageId,
254
                'SET[log_display]',
255
                $this->infoModuleController->MOD_SETTINGS['log_display'],
256
                $this->infoModuleController->MOD_MENU['log_display'],
257
                'index.php',
258
                '&setID=' . $setId
259
            );
260
    }
261
262
    private function getItemsPerPageDropDownHtml(): string
263
    {
264
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage') . ': ' .
265
            BackendUtility::getFuncMenu(
266
                $this->pageId,
267
                'SET[itemsPerPage]',
268
                $this->infoModuleController->MOD_SETTINGS['itemsPerPage'],
269
                $this->infoModuleController->MOD_MENU['itemsPerPage']
270
            );
271
    }
272
273
    private function getShowResultLogCheckBoxHtml(int $setId, string $quiPart): string
274
    {
275
        return BackendUtility::getFuncCheck(
276
                $this->pageId,
277
                'SET[log_resultLog]',
278
                $this->infoModuleController->MOD_SETTINGS['log_resultLog'],
279
                'index.php',
280
                '&setID=' . $setId . $quiPart
281
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showresultlog');
282
    }
283
284
    private function getShowFeVarsCheckBoxHtml(int $setId, string $quiPart): string
285
    {
286
        return BackendUtility::getFuncCheck(
287
                $this->pageId,
288
                'SET[log_feVars]',
289
                $this->infoModuleController->MOD_SETTINGS['log_feVars'],
290
                'index.php',
291
                '&setID=' . $setId . $quiPart
292
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showfevars');
293
    }
294
295
    /**
296
     * Create the rows for display of the page tree
297
     * For each page a number of rows are shown displaying GET variable configuration
298
     *
299
     * @param array $logEntriesOfPage Log items of one page
300
     * @param string $titleString Title string
301
     * @return string HTML <tr> content (one or more)
302
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
303
     */
304
    private function drawLog_addRows(array $logEntriesOfPage, string $titleString): string
305
    {
306
        $colSpan = 9
307
            + ($this->infoModuleController->MOD_SETTINGS['log_resultLog'] ? -1 : 0)
308
            + ($this->infoModuleController->MOD_SETTINGS['log_feVars'] ? 3 : 0);
309
310
        if (! empty($logEntriesOfPage)) {
311
            $setId = (int) GeneralUtility::_GP('setID');
312
            $refreshIcon = $this->getIconFactory()->getIcon('actions-system-refresh', Icon::SIZE_SMALL);
313
            // Traverse parameter combinations:
314
            $c = 0;
315
            $content = '';
316
            foreach ($logEntriesOfPage as $vv) {
317
                // Title column:
318
                if (! $c) {
319
                    $titleClm = '<td rowspan="' . count($logEntriesOfPage) . '">' . $titleString . '</td>';
320
                } else {
321
                    $titleClm = '';
322
                }
323
324
                // Result:
325
                $resLog = $this->getResultLog($vv);
326
327
                $resultData = $vv['result_data'] ? $this->jsonCompatibilityConverter->convert($vv['result_data']) : [];
328
                $resStatus = $this->getResStatus($resultData);
329
330
                // Compile row:
331
                $parameters = $this->jsonCompatibilityConverter->convert($vv['parameters']);
332
333
                // Put data into array:
334
                $rowData = [];
335
                if ($this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
336
                    $rowData['result_log'] = $resLog;
337
                } else {
338
                    $rowData['scheduled'] = ($vv['scheduled'] > 0) ? BackendUtility::datetime($vv['scheduled']) : ' ' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.immediate');
339
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
340
                }
341
                $rowData['result_status'] = GeneralUtility::fixed_lgd_cs($resStatus, 50);
342
                $url = htmlspecialchars($parameters['url'] ?? $parameters['alturl'], ENT_QUOTES | ENT_HTML5);
343
                $rowData['url'] = '<a href="' . $url . '" target="_newWIndow">' . $url . '</a>';
344
                $rowData['feUserGroupList'] = $parameters['feUserGroupList'] ?: '';
345
                $rowData['procInstructions'] = is_array($parameters['procInstructions']) ? implode('; ', $parameters['procInstructions']) : '';
346
                $rowData['set_id'] = (string) $vv['set_id'];
347
348
                if ($this->infoModuleController->MOD_SETTINGS['log_feVars']) {
349
                    $resFeVars = $this->getResFeVars($resultData ?: []);
0 ignored issues
show
Bug introduced by
It seems like $resultData ?: array() can also be of type true; however, parameter $resultData of AOE\Crawler\Backend\Requ...estForm::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

349
                    $resFeVars = $this->getResFeVars(/** @scrutinizer ignore-type */ $resultData ?: []);
Loading history...
350
                    $rowData['tsfe_id'] = $resFeVars['id'] ?: '';
351
                    $rowData['tsfe_gr_list'] = $resFeVars['gr_list'] ?: '';
352
                    $rowData['tsfe_no_cache'] = $resFeVars['no_cache'] ?: '';
353
                }
354
355
                $trClass = '';
356
                $warningIcon = '';
357
                if ($rowData['exec_time'] !== 0 && $resultData === false) {
358
                    $trClass = 'class="bg-danger"';
359
                    $warningIcon = $this->getIconFactory()->getIcon('actions-ban', Icon::SIZE_SMALL);
360
                }
361
362
                // Put rows together:
363
                $content .= '
364
                    <tr ' . $trClass . ' >
365
                        ' . $titleClm . '
366
                        <td><a href="' . UrlBuilder::getInfoModuleUrl(['qid_details' => $vv['qid'], 'setID' => $setId]) . '">' . htmlspecialchars((string) $vv['qid']) . '</a></td>
367
                        <td><a href="' . UrlBuilder::getInfoModuleUrl(['qid_read' => $vv['qid'], 'setID' => $setId]) . '">' . $refreshIcon . '</a>&nbsp;&nbsp;' . $warningIcon . '</td>';
368
                foreach ($rowData as $fKey => $value) {
369
                    if ($fKey === 'url') {
370
                        $content .= '<td>' . $value . '</td>';
371
                    } else {
372
                        $content .= '<td>' . nl2br(htmlspecialchars(strval($value))) . '</td>';
373
                    }
374
                }
375
                $content .= '</tr>';
376
                $c++;
377
378
                if ($this->CSVExport) {
379
                    // Only for CSV (adding qid and scheduled/exec_time if needed):
380
                    $rowData['result_log'] = implode('// ', explode(chr(10), $resLog));
381
                    $rowData['qid'] = $vv['qid'];
382
                    $rowData['scheduled'] = BackendUtility::datetime($vv['scheduled']);
383
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
384
                    $this->CSVaccu[] = $rowData;
385
                }
386
            }
387
        } else {
388
            // Compile row:
389
            $content = '
390
                <tr>
391
                    <td>' . $titleString . '</td>
392
                    <td colspan="' . $colSpan . '"><em>' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noentries') . '</em></td>
393
                </tr>';
394
        }
395
396
        return $content;
397
    }
398
399
    /**
400
     * Extract the log information from the current row and retrieve it as formatted string.
401
     *
402
     * @param array $resultRow
403
     * @return string
404
     */
405
    private function getResultLog($resultRow)
406
    {
407
        $content = '';
408
        if (is_array($resultRow) && array_key_exists('result_data', $resultRow)) {
409
            $requestContent = $this->jsonCompatibilityConverter->convert($resultRow['result_data']) ?: ['content' => ''];
410
            if (! array_key_exists('content', $requestContent)) {
0 ignored issues
show
Bug introduced by
It seems like $requestContent can also be of type true; however, parameter $search of array_key_exists() 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

410
            if (! array_key_exists('content', /** @scrutinizer ignore-type */ $requestContent)) {
Loading history...
411
                return $content;
412
            }
413
            $requestResult = $this->jsonCompatibilityConverter->convert($requestContent['content']);
414
415
            if (is_array($requestResult) && array_key_exists('log', $requestResult)) {
416
                $content = implode(chr(10), $requestResult['log']);
417
            }
418
        }
419
        return $content;
420
    }
421
422
    private function getResStatus($requestContent): string
423
    {
424
        if (empty($requestContent)) {
425
            return '-';
426
        }
427
        if (! array_key_exists('content', $requestContent)) {
428
            return 'Content index does not exists in requestContent array';
429
        }
430
431
        $requestResult = $this->jsonCompatibilityConverter->convert($requestContent['content']);
432
        if (is_array($requestResult)) {
433
            if (empty($requestResult['errorlog'])) {
434
                return 'OK';
435
            }
436
            return implode("\n", $requestResult['errorlog']);
437
        }
438
439
        if (is_bool($requestResult)) {
0 ignored issues
show
introduced by
The condition is_bool($requestResult) is always true.
Loading history...
440
            return 'Error - no info, sorry!';
441
        }
442
443
        return 'Error: ' . substr(preg_replace('/\s+/', ' ', strip_tags($requestResult)), 0, 10000) . '...';
444
    }
445
446
    /**
447
     * Find Fe vars
448
     */
449
    private function getResFeVars(array $resultData): array
450
    {
451
        if (empty($resultData)) {
452
            return [];
453
        }
454
        $requestResult = $this->jsonCompatibilityConverter->convert($resultData['content']);
455
        return $requestResult['vars'] ?? [];
456
    }
457
}
458