Completed
Push — refactor/backendModule-ValueOb... ( 037899...4f4abe )
by Tomas Norre
16:58
created

LogRequestForm::getResFeVars()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 2
cp 0
crap 6
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\Value\ModuleSettings;
0 ignored issues
show
Bug introduced by
The type AOE\Crawler\Value\ModuleSettings was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Doctrine\DBAL\Query\QueryBuilder;
12
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
13
use TYPO3\CMS\Backend\Utility\BackendUtility;
14
use TYPO3\CMS\Core\Database\ConnectionPool;
15
use TYPO3\CMS\Core\Imaging\Icon;
16
use TYPO3\CMS\Core\Imaging\IconFactory;
17
use TYPO3\CMS\Core\Localization\LanguageService;
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 RequestForm
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;
44
45
    /**
46
     * @var InfoModuleController
47
     */
48
    private $infoModuleController;
49
50
    /**
51
     * @var QueryBuilder
52
     */
53
    private $queryBuilder;
54
55
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController)
56
    {
57
        $this->view = $view;
58
        $this->infoModuleController = $infoModuleController;
59
        $this->jsonCompatibilityConverter = new JsonCompatibilityConverter();
60
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_crawler_queue');
61
        // TODO: Implement CSV Writer
62
    }
63
64
    public function render($id, string $currentValue, array $menuItems): string
65
    {
66
        $quiPart = GeneralUtility::_GP('qid_details') ? '&qid_details=' . (int) GeneralUtility::_GP('qid_details') : '';
67
        $setId = (int) GeneralUtility::_GP('setID');
68
        $this->pageId = $id;
69
70
        return $this->getDepthDropDownHtml($id, $currentValue, $menuItems)
71
            . $this->showLogAction($setId, $quiPart);
72
    }
73
74
    private function getDepthDropDownHtml($id, string $currentValue, array $menuItems): string
75
    {
76
        return BackendUtility::getFuncMenu(
77
            $id,
78
            'SET[depth]',
79
            $currentValue,
80
            $menuItems
81
        );
82
    }
83
84
    /*******************************
85
     *
86
     * Shows log of indexed URLs
87
     *
88
     ******************************/
89
90
    /**
91
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
92
     */
93
    private function showLogAction(int $setId, string $quiPath): string
94
    {
95
        $this->view->setTemplate('ShowLog');
96
        if (empty($this->pageId)) {
97
            $this->isErrorDetected = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isErrorDetected does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
98
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
99
        } else {
100
            $this->findCrawler()->setAccessMode('gui');
101
            $this->findCrawler()->setID = GeneralUtility::md5int(microtime());
102
103
            $csvExport = GeneralUtility::_POST('_csv');
104
            $this->CSVExport = isset($csvExport);
105
106
            // Read URL:
107
            if (GeneralUtility::_GP('qid_read')) {
108
                $this->findCrawler()->readUrl((int) GeneralUtility::_GP('qid_read'), true);
109
            }
110
111
            // Look for set ID sent - if it is, we will display contents of that set:
112
            $showSetId = (int) GeneralUtility::_GP('setID');
113
114
            $queueId = GeneralUtility::_GP('qid_details');
115
            $this->view->assign('queueId', $queueId);
116
            $this->view->assign('setId', $showSetId);
117
            // Show details:
118
            if ($queueId) {
119
                // Get entry record:
120
                $q_entry = $this->queryBuilder
121
                    ->from('tx_crawler_queue')
122
                    ->select('*')
123
                    ->where(
124
                        $this->queryBuilder->expr()->eq('qid', $this->queryBuilder->createNamedParameter($queueId))
125
                    )
126
                    ->execute()
127
                    ->fetch();
128
129
                // Explode values
130
                $q_entry['parameters'] = $this->jsonCompatibilityConverter->convert($q_entry['parameters']);
131
                $q_entry['result_data'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']);
132
                $resStatus = $this->getResStatus($q_entry['result_data']);
133
                if (is_array($q_entry['result_data'])) {
134
                    $q_entry['result_data']['content'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']['content']);
135
                    if (! $this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
136
                        unset($q_entry['result_data']['content']['log']);
137
                    }
138
                }
139
140
                $this->view->assign('queueStatus', $resStatus);
141
                $this->view->assign('queueDetails', DebugUtility::viewArray($q_entry));
142
            } else {
143
                // Show list
144
                // Drawing tree:
145
                $tree = GeneralUtility::makeInstance(PageTreeView::class);
146
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
147
                $tree->init('AND ' . $perms_clause);
148
149
                // Set root row:
150
                $pageinfo = BackendUtility::readPageAccess(
151
                    $this->pageId,
152
                    $perms_clause
153
                );
154
                $HTML = $this->getIconFactory()->getIconForRecord('pages', $pageinfo, Icon::SIZE_SMALL)->render();
155
                $tree->tree[] = [
156
                    'row' => $pageinfo,
157
                    'HTML' => $HTML,
158
                ];
159
160
                // Get branch beneath:
161
                if ($this->infoModuleController->MOD_SETTINGS['depth']) {
162
                    $tree->getTree($this->pageId, $this->infoModuleController->MOD_SETTINGS['depth']);
163
                }
164
165
                // If Flush button is pressed, flush tables instead of selecting entries:
166
                if (GeneralUtility::_POST('_flush')) {
167
                    $doFlush = true;
168
                    $doFullFlush = false;
169
                } elseif (GeneralUtility::_POST('_flush_all')) {
170
                    $doFlush = true;
171
                    $doFullFlush = true;
172
                } else {
173
                    $doFlush = false;
174
                    $doFullFlush = false;
175
                }
176
                $itemsPerPage = (int) $this->infoModuleController->MOD_SETTINGS['itemsPerPage'];
177
                // Traverse page tree:
178
                $code = '';
179
                $count = 0;
180
                foreach ($tree->tree as $data) {
181
                    // Get result:
182
                    $logEntriesOfPage = $this->crawlerController->getLogEntriesForPageId(
183
                        (int) $data['row']['uid'],
184
                        $this->infoModuleController->MOD_SETTINGS['log_display'],
185
                        $doFlush,
186
                        $doFullFlush,
187
                        $itemsPerPage
188
                    );
189
190
                    $code .= $this->drawLog_addRows(
191
                        $logEntriesOfPage,
192
                        $data['HTML'] . BackendUtility::getRecordTitle('pages', $data['row'], true)
193
                    );
194
                    if (++$count === 1000) {
195
                        break;
196
                    }
197
                }
198
                $this->view->assign('code', $code);
199
            }
200
201
            if ($this->CSVExport) {
202
                $this->outputCsvFile();
203
            }
204
        }
205
        $this->view->assign('showResultLog', (bool) $this->infoModuleController->MOD_SETTINGS['log_resultLog']);
206
        $this->view->assign('showFeVars', (bool) $this->infoModuleController->MOD_SETTINGS['log_feVars']);
207
        $this->view->assign('displayActions', 1);
208
        $this->view->assign('displayLogFilterHtml', $this->getDisplayLogFilterHtml($setId));
209
        $this->view->assign('itemPerPageHtml', $this->getItemsPerPageDropDownHtml());
210
        $this->view->assign('showResultLogHtml', $this->getShowResultLogCheckBoxHtml($setId, $quiPath));
211
        $this->view->assign('showFeVarsHtml', $this->getShowFeVarsCheckBoxHtml($setId, $quiPath));
212
        return $this->view->render();
213
    }
214
215
    /**
216
     * Outputs the CSV file and sets the correct headers
217
     */
218
    private function outputCsvFile(): void
219
    {
220
        if (! count($this->CSVaccu)) {
221
            MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.canNotExportEmptyQueueToCsvText'));
222
            return;
223
        }
224
225
        $csvString = $this->csvWriter->arrayToCsv($this->CSVaccu);
0 ignored issues
show
Bug Best Practice introduced by
The property csvWriter does not exist on AOE\Crawler\Backend\RequestForm\LogRequestForm. Did you maybe forget to declare it?
Loading history...
226
227
        header('Content-Type: application/octet-stream');
228
        header('Content-Disposition: attachment; filename=CrawlerLog.csv');
229
        echo $csvString;
230
231
        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...
232
    }
233
234
    private function getIconFactory(): IconFactory
235
    {
236
        return GeneralUtility::makeInstance(IconFactory::class);
237
    }
238
239
    private function getLanguageService(): LanguageService
240
    {
241
        return $GLOBALS['LANG'];
242
    }
243
244
    private function getDisplayLogFilterHtml(int $setId): string
245
    {
246
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.display') . ': ' . BackendUtility::getFuncMenu(
247
                $this->pageId,
248
                'SET[log_display]',
249
                $this->infoModuleController->MOD_SETTINGS['log_display'],
250
                $this->infoModuleController->MOD_MENU['log_display'],
251
                'index.php',
252
                '&setID=' . $setId
253
            );
254
    }
255
256
    private function getItemsPerPageDropDownHtml(): string
257
    {
258
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage') . ': ' .
259
            BackendUtility::getFuncMenu(
260
                $this->pageId,
261
                'SET[itemsPerPage]',
262
                $this->infoModuleController->MOD_SETTINGS['itemsPerPage'],
263
                $this->infoModuleController->MOD_MENU['itemsPerPage']
264
            );
265
    }
266
267
    private function getShowResultLogCheckBoxHtml(int $setId, string $quiPart): string
268
    {
269
        return BackendUtility::getFuncCheck(
270
                $this->pageId,
271
                'SET[log_resultLog]',
272
                $this->infoModuleController->MOD_SETTINGS['log_resultLog'],
273
                'index.php',
274
                '&setID=' . $setId . $quiPart
275
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showresultlog');
276
    }
277
278
    private function getShowFeVarsCheckBoxHtml(int $setId, string $quiPart): string
279
    {
280
        return BackendUtility::getFuncCheck(
281
                $this->pageId,
282
                'SET[log_feVars]',
283
                $this->infoModuleController->MOD_SETTINGS['log_feVars'],
284
                'index.php',
285
                '&setID=' . $setId . $quiPart
286
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showfevars');
287
    }
288
289
    /**
290
     * Create the rows for display of the page tree
291
     * For each page a number of rows are shown displaying GET variable configuration
292
     *
293
     * @param array $logEntriesOfPage Log items of one page
294
     * @param string $titleString Title string
295
     * @return string HTML <tr> content (one or more)
296
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
297
     */
298
    private function drawLog_addRows(array $logEntriesOfPage, string $titleString): string
299
    {
300
        $colSpan = 9
301
            + ($this->infoModuleController->MOD_SETTINGS['log_resultLog'] ? -1 : 0)
302
            + ($this->infoModuleController->MOD_SETTINGS['log_feVars'] ? 3 : 0);
303
304
        if (! empty($logEntriesOfPage)) {
305
            $setId = (int) GeneralUtility::_GP('setID');
306
            $refreshIcon = $this->getIconFactory()->getIcon('actions-system-refresh', Icon::SIZE_SMALL);
307
            // Traverse parameter combinations:
308
            $c = 0;
309
            $content = '';
310
            foreach ($logEntriesOfPage as $vv) {
311
                // Title column:
312
                if (! $c) {
313
                    $titleClm = '<td rowspan="' . count($logEntriesOfPage) . '">' . $titleString . '</td>';
314
                } else {
315
                    $titleClm = '';
316
                }
317
318
                // Result:
319
                $resLog = $this->getResultLog($vv);
320
321
                $resultData = $vv['result_data'] ? $this->jsonCompatibilityConverter->convert($vv['result_data']) : [];
322
                $resStatus = $this->getResStatus($resultData);
323
324
                // Compile row:
325
                $parameters = $this->jsonCompatibilityConverter->convert($vv['parameters']);
326
327
                // Put data into array:
328
                $rowData = [];
329
                if ($this->infoModuleController->MOD_SETTINGS['log_resultLog']) {
330
                    $rowData['result_log'] = $resLog;
331
                } else {
332
                    $rowData['scheduled'] = ($vv['scheduled'] > 0) ? BackendUtility::datetime($vv['scheduled']) : ' ' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.immediate');
333
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
334
                }
335
                $rowData['result_status'] = GeneralUtility::fixed_lgd_cs($resStatus, 50);
336
                $url = htmlspecialchars($parameters['url'] ?? $parameters['alturl'], ENT_QUOTES | ENT_HTML5);
337
                $rowData['url'] = '<a href="' . $url . '" target="_newWIndow">' . $url . '</a>';
338
                $rowData['feUserGroupList'] = $parameters['feUserGroupList'] ?: '';
339
                $rowData['procInstructions'] = is_array($parameters['procInstructions']) ? implode('; ', $parameters['procInstructions']) : '';
340
                $rowData['set_id'] = (string) $vv['set_id'];
341
342
                if ($this->infoModuleController->MOD_SETTINGS['log_feVars']) {
343
                    $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

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

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