Passed
Push — features/addFlushedPagesToCraw... ( 44f5f4...d700c8 )
by Tomas Norre
08:39
created

BackendModule::getDisplayLogFilterHtml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 1
dl 0
loc 9
ccs 0
cts 9
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Backend;
6
7
/***************************************************************
8
 *  Copyright notice
9
 *
10
 *  (c) 2020 AOE GmbH <[email protected]>
11
 *
12
 *  All rights reserved
13
 *
14
 *  This script is part of the TYPO3 project. The TYPO3 project is
15
 *  free software; you can redistribute it and/or modify
16
 *  it under the terms of the GNU General Public License as published by
17
 *  the Free Software Foundation; either version 3 of the License, or
18
 *  (at your option) any later version.
19
 *
20
 *  The GNU General Public License can be found at
21
 *  http://www.gnu.org/copyleft/gpl.html.
22
 *
23
 *  This script is distributed in the hope that it will be useful,
24
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 *  GNU General Public License for more details.
27
 *
28
 *  This copyright notice MUST APPEAR in all copies of the script!
29
 ***************************************************************/
30
31
use AOE\Crawler\Configuration\ExtensionConfigurationProvider;
32
use AOE\Crawler\Controller\CrawlerController;
33
use AOE\Crawler\Converter\JsonCompatibilityConverter;
34
use AOE\Crawler\Domain\Model\Reason;
35
use AOE\Crawler\Domain\Repository\ProcessRepository;
36
use AOE\Crawler\Domain\Repository\QueueRepository;
37
use AOE\Crawler\Hooks\CrawlerHookInterface;
38
use AOE\Crawler\Service\ProcessService;
39
use AOE\Crawler\Utility\MessageUtility;
40
use AOE\Crawler\Utility\PhpBinaryUtility;
41
use AOE\Crawler\Utility\SignalSlotUtility;
42
use Psr\Http\Message\UriInterface;
43
use TYPO3\CMS\Backend\Routing\UriBuilder;
44
use TYPO3\CMS\Backend\Template\ModuleTemplate;
45
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
46
use TYPO3\CMS\Backend\Utility\BackendUtility;
47
use TYPO3\CMS\Core\Database\ConnectionPool;
48
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
49
use TYPO3\CMS\Core\Http\Uri;
50
use TYPO3\CMS\Core\Imaging\Icon;
51
use TYPO3\CMS\Core\Imaging\IconFactory;
52
use TYPO3\CMS\Core\Localization\LanguageService;
53
use TYPO3\CMS\Core\Utility\CommandUtility;
54
use TYPO3\CMS\Core\Utility\CsvUtility;
55
use TYPO3\CMS\Core\Utility\DebugUtility;
56
use TYPO3\CMS\Core\Utility\GeneralUtility;
57
use TYPO3\CMS\Core\Utility\MathUtility;
58
use TYPO3\CMS\Extbase\Object\ObjectManager;
59
use TYPO3\CMS\Fluid\View\StandaloneView;
60
use TYPO3\CMS\Info\Controller\InfoModuleController;
61
62
/**
63
 * Function for Info module, containing three main actions:
64
 * - List of all queued items
65
 * - Log functionality
66
 * - Process overview
67
 */
68
class BackendModule
69
{
70
    /**
71
     * @var InfoModuleController Contains a reference to the parent calling object
72
     */
73
    protected $pObj;
74
75
    /**
76
     * The current page ID
77
     * @var int
78
     */
79
    protected $id;
80
81
    // Internal, dynamic:
82
83
    /**
84
     * @var array
85
     */
86
    protected $duplicateTrack = [];
87
88
    /**
89
     * @var bool
90
     */
91
    protected $submitCrawlUrls = false;
92
93
    /**
94
     * @var bool
95
     */
96
    protected $downloadCrawlUrls = false;
97
98
    /**
99
     * @var int
100
     */
101
    protected $scheduledTime = 0;
102
103
    /**
104
     * @var int
105
     */
106
    protected $reqMinute = 1000;
107
108
    /**
109
     * @var array holds the selection of configuration from the configuration selector box
110
     */
111
    protected $incomingConfigurationSelection = [];
112
113
    /**
114
     * @var CrawlerController
115
     */
116
    protected $crawlerController;
117
118
    /**
119
     * @var array
120
     */
121
    protected $CSVaccu = [];
122
123
    /**
124
     * If true the user requested a CSV export of the queue
125
     *
126
     * @var boolean
127
     */
128
    protected $CSVExport = false;
129
130
    /**
131
     * @var array
132
     */
133
    protected $downloadUrls = [];
134
135
    /**
136
     * Holds the configuration from ext_conf_template loaded by getExtensionConfiguration()
137
     *
138
     * @var array
139
     */
140
    protected $extensionSettings = [];
141
142
    /**
143
     * Indicate that an flash message with an error is present.
144
     *
145
     * @var boolean
146
     */
147
    protected $isErrorDetected = false;
148
149
    /**
150
     * @var ProcessService
151
     */
152
    protected $processManager;
153
154
    /**
155
     * @var QueryBuilder
156
     */
157
    protected $queryBuilder;
158
159
    /**
160
     * @var QueueRepository
161
     */
162
    protected $queueRepository;
163
164
    /**
165
     * @var StandaloneView
166
     */
167
    protected $view;
168
169
    /**
170
     * @var IconFactory
171
     */
172
    protected $iconFactory;
173
174
    /**
175
     * @var JsonCompatibilityConverter
176
     */
177
    protected $jsonCompatibilityConverter;
178
179
    public function __construct()
180
    {
181
        $objectManger = GeneralUtility::makeInstance(ObjectManager::class);
182
        $this->processManager = $objectManger->get(ProcessService::class);
183
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_crawler_queue');
184
        $this->queueRepository = $objectManger->get(QueueRepository::class);
185
        $this->initializeView();
186
        $this->extensionSettings = GeneralUtility::makeInstance(ExtensionConfigurationProvider::class)->getExtensionConfiguration();
187
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
188
        $this->jsonCompatibilityConverter = GeneralUtility::makeInstance(JsonCompatibilityConverter::class);
189
    }
190
191
    /**
192
     * Called by the InfoModuleController
193
     */
194
    public function init(InfoModuleController $pObj): void
195
    {
196
        $this->pObj = $pObj;
197
        $this->id = (int) GeneralUtility::_GP('id');
198
        // Setting MOD_MENU items as we need them for logging:
199
        $this->pObj->MOD_MENU = array_merge($this->pObj->MOD_MENU, $this->modMenu());
200
    }
201
202
    /**
203
     * Additions to the function menu array
204
     *
205
     * @return array Menu array
206
     */
207 1
    public function modMenu(): array
208
    {
209
        return [
210
            'depth' => [
211 1
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
212 1
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
213 1
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
214 1
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
215 1
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
216 1
                99 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
217
            ],
218
            'crawlaction' => [
219 1
                'start' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.start'),
220 1
                'log' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.log'),
221 1
                'multiprocess' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.multiprocess'),
222
            ],
223 1
            'log_resultLog' => '',
224 1
            'log_feVars' => '',
225 1
            'processListMode' => '',
226
            'log_display' => [
227 1
                'all' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.all'),
228 1
                'pending' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.pending'),
229 1
                'finished' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.finished'),
230
            ],
231
            'itemsPerPage' => [
232 1
                '5' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage.5'),
233 1
                '10' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage.10'),
234 1
                '50' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage.50'),
235 1
                '0' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage.0'),
236
            ],
237
        ];
238
    }
239
240
    public function main(): string
241
    {
242
        if (empty($this->pObj->MOD_SETTINGS['processListMode'])) {
243
            $this->pObj->MOD_SETTINGS['processListMode'] = 'simple';
244
        }
245
        $this->view->assign('currentPageId', $this->id);
246
247
        $selectedAction = (string) $this->pObj->MOD_SETTINGS['crawlaction'] ?? 'start';
248
249
        // Type function menu:
250
        $actionDropdown = BackendUtility::getFuncMenu(
251
            $this->id,
252
            'SET[crawlaction]',
253
            $selectedAction,
254
            $this->pObj->MOD_MENU['crawlaction']
255
        );
256
257
        $theOutput = '<h2>' . htmlspecialchars($this->getLanguageService()->getLL('title')) . '</h2>' . $actionDropdown;
258
259
        // Branch based on type:
260
        switch ($selectedAction) {
261
            case 'log':
262
                $quiPart = GeneralUtility::_GP('qid_details') ? '&qid_details=' . (int) GeneralUtility::_GP('qid_details') : '';
263
                $setId = (int) GeneralUtility::_GP('setID');
264
265
                // Additional menus for the log type:
266
                $theOutput .= $this->getDepthDropDownHtml();
267
                $theOutput .= $this->showLogAction($setId, $quiPart);
268
                break;
269
            case 'multiprocess':
270
                $theOutput .= $this->processOverviewAction();
271
                break;
272
            case 'start':
273
            default:
274
                $theOutput .= $this->showCrawlerInformationAction();
275
                break;
276
        }
277
278
        return $theOutput;
279
    }
280
281
    /*******************************
282
     *
283
     * Generate URLs for crawling:
284
     *
285
     ******************************/
286
287
    /**
288
     * Show a list of URLs to be crawled for each page
289
     */
290
    protected function showCrawlerInformationAction(): string
291
    {
292
        $this->view->setTemplate('ShowCrawlerInformation');
293
        if (empty($this->id)) {
294
            $this->isErrorDetected = true;
295
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
296
        } else {
297
            $crawlerParameter = GeneralUtility::_GP('_crawl');
298
            $downloadParameter = GeneralUtility::_GP('_download');
299
300
            $this->duplicateTrack = [];
301
            $this->submitCrawlUrls = isset($crawlerParameter);
302
            $this->downloadCrawlUrls = isset($downloadParameter);
303
            $this->makeCrawlerProcessableChecks();
304
305
            switch ((string) GeneralUtility::_GP('tstamp')) {
306
                case 'midnight':
307
                    $this->scheduledTime = mktime(0, 0, 0);
308
                    break;
309
                case '04:00':
310
                    $this->scheduledTime = mktime(0, 0, 0) + 4 * 3600;
311
                    break;
312
                case 'now':
313
                default:
314
                    $this->scheduledTime = time();
315
                    break;
316
            }
317
318
            $this->incomingConfigurationSelection = GeneralUtility::_GP('configurationSelection');
319
            $this->incomingConfigurationSelection = is_array($this->incomingConfigurationSelection) ? $this->incomingConfigurationSelection : [];
320
321
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
322
            $this->crawlerController->setAccessMode('gui');
323
            $this->crawlerController->setID = GeneralUtility::md5int(microtime());
324
325
            $code = '';
326
            $noConfigurationSelected = empty($this->incomingConfigurationSelection)
327
                || (count($this->incomingConfigurationSelection) === 1 && empty($this->incomingConfigurationSelection[0]));
328
            if ($noConfigurationSelected) {
329
                MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noConfigSelected'));
330
            } else {
331
                if ($this->submitCrawlUrls) {
332
                    $reason = new Reason();
333
                    $reason->setReason(Reason::REASON_GUI_SUBMIT);
334
                    $reason->setDetailText('The user ' . $GLOBALS['BE_USER']->user['username'] . ' added pages to the crawler queue manually');
335
336
                    $signalPayload = ['reason' => $reason];
337
                    SignalSlotUtility::emitSignal(
338
                        self::class,
339
                        SignalSlotUtility::SIGNAL_INVOKE_QUEUE_CHANGE,
340
                        $signalPayload
341
                    );
342
                }
343
344
                $code = $this->crawlerController->getPageTreeAndUrls(
345
                    $this->id,
346
                    $this->pObj->MOD_SETTINGS['depth'],
347
                    $this->scheduledTime,
348
                    $this->reqMinute,
349
                    $this->submitCrawlUrls,
350
                    $this->downloadCrawlUrls,
351
                    [], // Do not filter any processing instructions
352
                    $this->incomingConfigurationSelection
353
                );
354
            }
355
356
            $this->downloadUrls = $this->crawlerController->downloadUrls;
357
            $this->duplicateTrack = $this->crawlerController->duplicateTrack;
358
359
            $this->view->assign('noConfigurationSelected', $noConfigurationSelected);
360
            $this->view->assign('submitCrawlUrls', $this->submitCrawlUrls);
361
            $this->view->assign('amountOfUrls', count(array_keys($this->duplicateTrack)));
362
            $this->view->assign('selectors', $this->generateConfigurationSelectors());
363
            $this->view->assign('code', $code);
364
            $this->view->assign('displayActions', 0);
365
366
            // Download Urls to crawl:
367
            if ($this->downloadCrawlUrls) {
368
                // Creating output header:
369
                header('Content-Type: application/octet-stream');
370
                header('Content-Disposition: attachment; filename=CrawlerUrls.txt');
371
372
                // Printing the content of the CSV lines:
373
                echo implode(chr(13) . chr(10), $this->downloadUrls);
374
                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...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
375
            }
376
        }
377
        return $this->view->render();
378
    }
379
380
    /**
381
     * Generates the configuration selectors for compiling URLs:
382
     */
383
    protected function generateConfigurationSelectors(): array
384
    {
385
        $selectors = [];
386
        $selectors['depth'] = $this->selectorBox(
387
            [
388
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
389
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
390
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
391
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
392
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
393
                99 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
394
            ],
395
            'SET[depth]',
396
            $this->pObj->MOD_SETTINGS['depth'],
397
            false
398
        );
399
400
        // Configurations
401
        $availableConfigurations = $this->crawlerController->getConfigurationsForBranch((int) $this->id, (int) $this->pObj->MOD_SETTINGS['depth'] ?: 0);
402
        $selectors['configurations'] = $this->selectorBox(
403
            empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
0 ignored issues
show
Bug introduced by
It seems like empty($availableConfigur...vailableConfigurations) can also be of type false; however, parameter $optArray of AOE\Crawler\Backend\BackendModule::selectorBox() 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

403
            /** @scrutinizer ignore-type */ empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
Loading history...
404
            'configurationSelection',
405
            $this->incomingConfigurationSelection,
406
            true
407
        );
408
409
        // Scheduled time:
410
        $selectors['scheduled'] = $this->selectorBox(
411
            [
412
                'now' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.now'),
413
                'midnight' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.midnight'),
414
                '04:00' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.4am'),
415
            ],
416
            'tstamp',
417
            GeneralUtility::_POST('tstamp'),
418
            false
419
        );
420
421
        return $selectors;
422
    }
423
424
    /*******************************
425
     *
426
     * Shows log of indexed URLs
427
     *
428
     ******************************/
429
430
    /**
431
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
432
     */
433
    protected function showLogAction(int $setId, string $quiPath): string
434
    {
435
        $this->view->setTemplate('ShowLog');
436
        if (empty($this->id)) {
437
            $this->isErrorDetected = true;
438
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
439
        } else {
440
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
441
            $this->crawlerController->setAccessMode('gui');
442
            $this->crawlerController->setID = GeneralUtility::md5int(microtime());
443
444
            $csvExport = GeneralUtility::_POST('_csv');
445
            $this->CSVExport = isset($csvExport);
446
447
            // Read URL:
448
            if (GeneralUtility::_GP('qid_read')) {
449
                $this->crawlerController->readUrl((int) GeneralUtility::_GP('qid_read'), true);
450
            }
451
452
            // Look for set ID sent - if it is, we will display contents of that set:
453
            $showSetId = (int) GeneralUtility::_GP('setID');
454
455
            $queueId = GeneralUtility::_GP('qid_details');
456
            $this->view->assign('queueId', $queueId);
457
            $this->view->assign('setId', $showSetId);
458
            // Show details:
459
            if ($queueId) {
460
                // Get entry record:
461
                $q_entry = $this->queryBuilder
462
                    ->from('tx_crawler_queue')
463
                    ->select('*')
464
                    ->where(
465
                        $this->queryBuilder->expr()->eq('qid', $this->queryBuilder->createNamedParameter($queueId))
466
                    )
467
                    ->execute()
468
                    ->fetch();
469
470
                // Explode values
471
                $q_entry['parameters'] = $this->jsonCompatibilityConverter->convert($q_entry['parameters']);
472
                $q_entry['result_data'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']);
473
                $resStatus = $this->getResStatus($q_entry['result_data']);
474
                if (is_array($q_entry['result_data'])) {
475
                    $q_entry['result_data']['content'] = $this->jsonCompatibilityConverter->convert($q_entry['result_data']['content']);
476
                    if (! $this->pObj->MOD_SETTINGS['log_resultLog']) {
477
                        unset($q_entry['result_data']['content']['log']);
478
                    }
479
                }
480
481
                $this->view->assign('queueStatus', $resStatus);
482
                $this->view->assign('queueDetails', DebugUtility::viewArray($q_entry));
483
            } else {
484
                // Show list
485
                // Drawing tree:
486
                $tree = GeneralUtility::makeInstance(PageTreeView::class);
487
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
488
                $tree->init('AND ' . $perms_clause);
489
490
                // Set root row:
491
                $pageinfo = BackendUtility::readPageAccess(
492
                    $this->id,
493
                    $perms_clause
494
                );
495
                $HTML = $this->iconFactory->getIconForRecord('pages', $pageinfo, Icon::SIZE_SMALL)->render();
496
                $tree->tree[] = [
497
                    'row' => $pageinfo,
498
                    'HTML' => $HTML,
499
                ];
500
501
                // Get branch beneath:
502
                if ($this->pObj->MOD_SETTINGS['depth']) {
503
                    $tree->getTree($this->id, $this->pObj->MOD_SETTINGS['depth']);
504
                }
505
506
                // If Flush button is pressed, flush tables instead of selecting entries:
507
                if (GeneralUtility::_POST('_flush')) {
508
                    $doFlush = true;
509
                    $doFullFlush = false;
510
                } elseif (GeneralUtility::_POST('_flush_all')) {
511
                    $doFlush = true;
512
                    $doFullFlush = true;
513
                } else {
514
                    $doFlush = false;
515
                    $doFullFlush = false;
516
                }
517
                $itemsPerPage = (int) $this->pObj->MOD_SETTINGS['itemsPerPage'];
518
                // Traverse page tree:
519
                $code = '';
520
                $count = 0;
521
                foreach ($tree->tree as $data) {
522
                    // Get result:
523
                    $logEntriesOfPage = $this->crawlerController->getLogEntriesForPageId(
524
                        (int) $data['row']['uid'],
525
                        $this->pObj->MOD_SETTINGS['log_display'],
526
                        $doFlush,
527
                        $doFullFlush,
528
                        $itemsPerPage
529
                    );
530
531
                    $code .= $this->drawLog_addRows(
532
                        $logEntriesOfPage,
533
                        $data['HTML'] . BackendUtility::getRecordTitle('pages', $data['row'], true)
534
                    );
535
                    if (++$count === 1000) {
536
                        break;
537
                    }
538
                }
539
                $this->view->assign('code', $code);
540
            }
541
542
            if ($this->CSVExport) {
543
                $this->outputCsvFile();
544
            }
545
        }
546
        $this->view->assign('showResultLog', (bool) $this->pObj->MOD_SETTINGS['log_resultLog']);
547
        $this->view->assign('showFeVars', (bool) $this->pObj->MOD_SETTINGS['log_feVars']);
548
        $this->view->assign('displayActions', 1);
549
        $this->view->assign('displayLogFilterHtml', $this->getDisplayLogFilterHtml($setId));
550
        $this->view->assign('itemPerPageHtml', $this->getItemsPerPageDropDownHtml());
551
        $this->view->assign('showResultLogHtml', $this->getShowResultLogCheckBoxHtml($setId, $quiPath));
552
        $this->view->assign('showFeVarsHtml', $this->getShowFeVarsCheckBoxHtml($setId, $quiPath));
553
        return $this->view->render();
554
    }
555
556
    /**
557
     * Outputs the CSV file and sets the correct headers
558
     */
559
    protected function outputCsvFile(): void
560
    {
561
        if (! count($this->CSVaccu)) {
562
            MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.canNotExportEmptyQueueToCsvText'));
563
            return;
564
        }
565
        $csvLines = [];
566
567
        // Field names:
568
        reset($this->CSVaccu);
569
        $fieldNames = array_keys(current($this->CSVaccu));
570
        $csvLines[] = CsvUtility::csvValues($fieldNames);
571
572
        // Data:
573
        foreach ($this->CSVaccu as $row) {
574
            $csvLines[] = CsvUtility::csvValues($row);
575
        }
576
577
        // Creating output header:
578
        header('Content-Type: application/octet-stream');
579
        header('Content-Disposition: attachment; filename=CrawlerLog.csv');
580
581
        // Printing the content of the CSV lines:
582
        echo implode(chr(13) . chr(10), $csvLines);
583
        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...
584
    }
585
586
    /**
587
     * Create the rows for display of the page tree
588
     * For each page a number of rows are shown displaying GET variable configuration
589
     *
590
     * @param array $logEntriesOfPage Log items of one page
591
     * @param string $titleString Title string
592
     * @return string HTML <tr> content (one or more)
593
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
594
     */
595
    protected function drawLog_addRows(array $logEntriesOfPage, string $titleString): string
596
    {
597
        $colSpan = 9
598
            + ($this->pObj->MOD_SETTINGS['log_resultLog'] ? -1 : 0)
599
            + ($this->pObj->MOD_SETTINGS['log_feVars'] ? 3 : 0);
600
601
        if (! empty($logEntriesOfPage)) {
602
            $setId = (int) GeneralUtility::_GP('setID');
603
            $refreshIcon = $this->iconFactory->getIcon('actions-system-refresh', Icon::SIZE_SMALL);
604
            // Traverse parameter combinations:
605
            $c = 0;
606
            $content = '';
607
            foreach ($logEntriesOfPage as $vv) {
608
                // Title column:
609
                if (! $c) {
610
                    $titleClm = '<td rowspan="' . count($logEntriesOfPage) . '">' . $titleString . '</td>';
611
                } else {
612
                    $titleClm = '';
613
                }
614
615
                // Result:
616
                $resLog = $this->getResultLog($vv);
617
618
                $resultData = $vv['result_data'] ? $this->jsonCompatibilityConverter->convert($vv['result_data']) : [];
619
                $resStatus = $this->getResStatus($resultData);
620
621
                // Compile row:
622
                $parameters = $this->jsonCompatibilityConverter->convert($vv['parameters']);
623
624
                // Put data into array:
625
                $rowData = [];
626
                if ($this->pObj->MOD_SETTINGS['log_resultLog']) {
627
                    $rowData['result_log'] = $resLog;
628
                } else {
629
                    $rowData['scheduled'] = ($vv['scheduled'] > 0) ? BackendUtility::datetime($vv['scheduled']) : ' ' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.immediate');
630
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
631
                }
632
                $rowData['result_status'] = GeneralUtility::fixed_lgd_cs($resStatus, 50);
633
                $url = htmlspecialchars($parameters['url'] ?? $parameters['alturl']);
634
                $rowData['url'] = '<a href="' . $url . '" target="_newWIndow">' . $url . '</a>';
635
                $rowData['feUserGroupList'] = $parameters['feUserGroupList'] ?: '';
636
                $rowData['procInstructions'] = is_array($parameters['procInstructions']) ? implode('; ', $parameters['procInstructions']) : '';
637
                $rowData['set_id'] = (string) $vv['set_id'];
638
639
                if ($this->pObj->MOD_SETTINGS['log_feVars']) {
640
                    $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\BackendModule::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

640
                    $resFeVars = $this->getResFeVars(/** @scrutinizer ignore-type */ $resultData ?: []);
Loading history...
641
                    $rowData['tsfe_id'] = $resFeVars['id'] ?: '';
642
                    $rowData['tsfe_gr_list'] = $resFeVars['gr_list'] ?: '';
643
                    $rowData['tsfe_no_cache'] = $resFeVars['no_cache'] ?: '';
644
                }
645
646
                // Put rows together:
647
                $content .= '
648
                    <tr>
649
                        ' . $titleClm . '
650
                        <td><a href="' . $this->getInfoModuleUrl(['qid_details' => $vv['qid'], 'setID' => $setId]) . '">' . htmlspecialchars((string) $vv['qid']) . '</a></td>
651
                        <td><a href="' . $this->getInfoModuleUrl(['qid_read' => $vv['qid'], 'setID' => $setId]) . '">' . $refreshIcon . '</a></td>';
652
                foreach ($rowData as $fKey => $value) {
653
                    if ($fKey === 'url') {
654
                        $content .= '<td>' . $value . '</td>';
655
                    } else {
656
                        $content .= '<td>' . nl2br(htmlspecialchars(strval($value))) . '</td>';
657
                    }
658
                }
659
                $content .= '</tr>';
660
                $c++;
661
662
                if ($this->CSVExport) {
663
                    // Only for CSV (adding qid and scheduled/exec_time if needed):
664
                    $rowData['result_log'] = implode('// ', explode(chr(10), $resLog));
665
                    $rowData['qid'] = $vv['qid'];
666
                    $rowData['scheduled'] = BackendUtility::datetime($vv['scheduled']);
667
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
668
                    $this->CSVaccu[] = $rowData;
669
                }
670
            }
671
        } else {
672
            // Compile row:
673
            $content = '
674
                <tr>
675
                    <td>' . $titleString . '</td>
676
                    <td colspan="' . $colSpan . '"><em>' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noentries') . '</em></td>
677
                </tr>';
678
        }
679
680
        return $content;
681
    }
682
683
    /**
684
     * Find Fe vars
685
     */
686 4
    protected function getResFeVars(array $resultData): array
687
    {
688 4
        if (empty($resultData)) {
689 1
            return [];
690
        }
691 3
        $requestResult = $this->jsonCompatibilityConverter->convert($resultData['content']);
692 3
        return $requestResult['vars'] ?? [];
693
    }
694
695
    /**
696
     * Extract the log information from the current row and retrieve it as formatted string.
697
     *
698
     * @param array $resultRow
699
     * @return string
700
     */
701 6
    protected function getResultLog($resultRow)
702
    {
703 6
        $content = '';
704 6
        if (is_array($resultRow) && array_key_exists('result_data', $resultRow)) {
705 5
            $requestContent = $this->jsonCompatibilityConverter->convert($resultRow['result_data']) ?: ['content' => ''];
706 5
            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

706
            if (! array_key_exists('content', /** @scrutinizer ignore-type */ $requestContent)) {
Loading history...
707 1
                return $content;
708
            }
709 4
            $requestResult = $this->jsonCompatibilityConverter->convert($requestContent['content']);
710
711 4
            if (is_array($requestResult) && array_key_exists('log', $requestResult)) {
712 2
                $content = implode(chr(10), $requestResult['log']);
713
            }
714
        }
715 5
        return $content;
716
    }
717
718 7
    protected function getResStatus($requestContent): string
719
    {
720 7
        if (empty($requestContent)) {
721 2
            return '-';
722
        }
723 5
        if (! array_key_exists('content', $requestContent)) {
724 1
            return 'Content index does not exists in requestContent array';
725
        }
726
727 4
        $requestResult = $this->jsonCompatibilityConverter->convert($requestContent['content']);
728 4
        if (is_array($requestResult)) {
729 3
            if (empty($requestResult['errorlog'])) {
730 1
                return 'OK';
731
            }
732 2
            return implode("\n", $requestResult['errorlog']);
733
        }
734
735 1
        if (is_bool($requestResult)) {
0 ignored issues
show
introduced by
The condition is_bool($requestResult) is always true.
Loading history...
736 1
            return 'Error - no info, sorry!';
737
        }
738
739
        return 'Error: ' . substr(preg_replace('/\s+/', ' ', strip_tags($requestResult)), 0, 10000) . '...';
740
    }
741
742
    /**
743
     * This method is used to show an overview about the active an the finished crawling processes
744
     *
745
     * @return string
746
     */
747
    protected function processOverviewAction()
748
    {
749
        $this->view->setTemplate('ProcessOverview');
750
        $this->runRefreshHooks();
751
        $this->makeCrawlerProcessableChecks();
752
753
        try {
754
            $this->handleProcessOverviewActions();
755
        } catch (\Throwable $e) {
756
            $this->isErrorDetected = true;
757
            MessageUtility::addErrorMessage($e->getMessage());
758
        }
759
760
        $processRepository = new ProcessRepository();
761
        $queueRepository = new QueueRepository();
762
763
        $mode = $this->pObj->MOD_SETTINGS['processListMode'];
764
        if ($mode === 'simple') {
765
            $allProcesses = $processRepository->findAllActive();
766
        } else {
767
            $allProcesses = $processRepository->findAll();
768
        }
769
        $isCrawlerEnabled = ! $this->findCrawler()->getDisabled() && ! $this->isErrorDetected;
770
        $currentActiveProcesses = $processRepository->countActive();
771
        $maxActiveProcesses = MathUtility::forceIntegerInRange($this->extensionSettings['processLimit'], 1, 99, 1);
772
        $this->view->assignMultiple([
773
            'pageId' => (int) $this->id,
774
            'refreshLink' => $this->getRefreshLink(),
775
            'addLink' => $this->getAddLink($currentActiveProcesses, $maxActiveProcesses, $isCrawlerEnabled),
776
            'modeLink' => $this->getModeLink($mode),
777
            'enableDisableToggle' => $this->getEnableDisableLink($isCrawlerEnabled),
778
            'processCollection' => $allProcesses,
779
            'cliPath' => $this->processManager->getCrawlerCliPath(),
780
            'isCrawlerEnabled' => $isCrawlerEnabled,
781
            'totalUnprocessedItemCount' => $queueRepository->countAllPendingItems(),
782
            'assignedUnprocessedItemCount' => $queueRepository->countAllAssignedPendingItems(),
783
            'activeProcessCount' => $currentActiveProcesses,
784
            'maxActiveProcessCount' => $maxActiveProcesses,
785
            'mode' => $mode,
786
            'displayActions' => 0,
787
        ]);
788
789
        return $this->view->render();
790
    }
791
792
    /**
793
     * Returns a tag for the refresh icon
794
     *
795
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
796
     */
797
    protected function getRefreshLink(): string
798
    {
799
        return $this->getLinkButton(
800
            'actions-refresh',
801
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.refresh'),
802
            $this->getInfoModuleUrl(['SET[\'crawleraction\']' => 'crawleraction', 'id' => $this->id])
803
        );
804
    }
805
806
    /**
807
     * Returns a link for the panel to enable or disable the crawler
808
     *
809
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
810
     */
811
    protected function getEnableDisableLink(bool $isCrawlerEnabled): string
812
    {
813
        if ($isCrawlerEnabled) {
814
            return $this->getLinkButton(
815
                'tx-crawler-stop',
816
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.disablecrawling'),
817
                $this->getInfoModuleUrl(['action' => 'stopCrawling'])
818
            );
819
        }
820
        return $this->getLinkButton(
821
            'tx-crawler-start',
822
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.enablecrawling'),
823
            $this->getInfoModuleUrl(['action' => 'resumeCrawling'])
824
        );
825
    }
826
827
    /**
828
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
829
     */
830
    protected function getModeLink(string $mode): string
831
    {
832
        if ($mode === 'detail') {
833
            return $this->getLinkButton(
834
                'actions-document-view',
835
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.running'),
836
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'simple'])
837
            );
838
        } elseif ($mode === 'simple') {
839
            return $this->getLinkButton(
840
                'actions-document-view',
841
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.all'),
842
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'detail'])
843
            );
844
        }
845
        return '';
846
    }
847
848
    /**
849
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
850
     */
851
    protected function getAddLink(int $currentActiveProcesses, int $maxActiveProcesses, bool $isCrawlerEnabled): string
852
    {
853
        if (! $isCrawlerEnabled) {
854
            return '';
855
        }
856
        if ($currentActiveProcesses >= $maxActiveProcesses) {
857
            return '';
858
        }
859
860
        return $this->getLinkButton(
861
            'actions-add',
862
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.process.add'),
863
            $this->getInfoModuleUrl(['action' => 'addProcess'])
864
        );
865
    }
866
867
    /**
868
     * Verify that the crawler is executable.
869
     */
870
    protected function makeCrawlerProcessableChecks(): void
871
    {
872
        if (! $this->isPhpForkAvailable()) {
873
            $this->isErrorDetected = true;
874
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.noPhpForkAvailable'));
875
        }
876
877
        $exitCode = 0;
878
        $out = [];
879
        CommandUtility::exec(
880
            PhpBinaryUtility::getPhpBinary() . ' -v',
881
            $out,
882
            $exitCode
883
        );
884
        if ($exitCode > 0) {
885
            $this->isErrorDetected = true;
886
            MessageUtility::addErrorMessage(sprintf($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.phpBinaryNotFound'), htmlspecialchars($this->extensionSettings['phpPath'])));
887
        }
888
    }
889
890
    /**
891
     * Indicate that the required PHP method "popen" is
892
     * available in the system.
893
     */
894
    protected function isPhpForkAvailable(): bool
895
    {
896
        return function_exists('popen');
897
    }
898
899
    /**
900
     * Method to handle incomming actions of the process overview
901
     *
902
     * @throws ProcessException
903
     */
904
    protected function handleProcessOverviewActions(): void
905
    {
906
        $crawler = $this->findCrawler();
907
908
        switch (GeneralUtility::_GP('action')) {
909
            case 'stopCrawling':
910
                //set the cli status to disable (all processes will be terminated)
911
                $crawler->setDisabled(true);
912
                break;
913
            case 'addProcess':
914
                if ($this->processManager->startProcess() === false) {
0 ignored issues
show
introduced by
The condition $this->processManager->startProcess() === false is always false.
Loading history...
915
                    throw new ProcessException($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocesserror'));
0 ignored issues
show
Bug introduced by
The type AOE\Crawler\Backend\ProcessException 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...
916
                }
917
                MessageUtility::addNoticeMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocess'));
918
                break;
919
            case 'resumeCrawling':
920
            default:
921
                //set the cli status to end (all processes will be terminated)
922
                $crawler->setDisabled(false);
923
                break;
924
        }
925
    }
926
927
    /**
928
     * Returns the singleton instance of the crawler.
929
     */
930
    protected function findCrawler(): CrawlerController
931
    {
932
        if (! $this->crawlerController instanceof CrawlerController) {
0 ignored issues
show
introduced by
$this->crawlerController is always a sub-type of AOE\Crawler\Controller\CrawlerController.
Loading history...
933
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
934
        }
935
        return $this->crawlerController;
936
    }
937
938
    /*****************************
939
     *
940
     * General Helper Functions
941
     *
942
     *****************************/
943
944
    /**
945
     * Create selector box
946
     *
947
     * @param array $optArray Options key(value) => label pairs
948
     * @param string $name Selector box name
949
     * @param string|array $value Selector box value (array for multiple...)
950
     * @param boolean $multiple If set, will draw multiple box.
951
     *
952
     * @return string HTML select element
953
     */
954
    protected function selectorBox($optArray, $name, $value, bool $multiple): string
955
    {
956
        if (! is_string($value) || ! is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
957
            $value = '';
958
        }
959
960
        $options = [];
961
        foreach ($optArray as $key => $val) {
962
            $selected = (! $multiple && ! strcmp($value, (string) $key)) || ($multiple && in_array($key, (array) $value, true));
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $selected = (! $multiple..., (array)$value, true)), Probably Intended Meaning: $selected = ! $multiple ..., (array)$value, true))
Loading history...
963
            $options[] = '
964
                <option value="' . $key . '" ' . ($selected ? ' selected="selected"' : '') . '>' . htmlspecialchars($val) . '</option>';
965
        }
966
967
        return '<select class="form-control" name="' . htmlspecialchars($name . ($multiple ? '[]' : '')) . '"' . ($multiple ? ' multiple' : '') . '>' . implode('', $options) . '</select>';
968
    }
969
970
    /**
971
     * Activate hooks
972
     */
973
    protected function runRefreshHooks(): void
974
    {
975
        $crawlerLib = GeneralUtility::makeInstance(CrawlerController::class);
976
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['crawler']['refresh_hooks'] ?? [] as $objRef) {
977
            /** @var CrawlerHookInterface $hookObj */
978
            $hookObj = GeneralUtility::makeInstance($objRef);
979
            if (is_object($hookObj)) {
980
                $hookObj->crawler_init($crawlerLib);
981
            }
982
        }
983
    }
984
985
    protected function initializeView(): void
986
    {
987
        $view = GeneralUtility::makeInstance(StandaloneView::class);
988
        $view->setLayoutRootPaths(['EXT:crawler/Resources/Private/Layouts']);
989
        $view->setPartialRootPaths(['EXT:crawler/Resources/Private/Partials']);
990
        $view->setTemplateRootPaths(['EXT:crawler/Resources/Private/Templates/Backend']);
991
        $view->getRequest()->setControllerExtensionName('Crawler');
992
        $this->view = $view;
993
    }
994
995
    protected function getLanguageService(): LanguageService
996
    {
997
        return $GLOBALS['LANG'];
998
    }
999
1000
    protected function getLinkButton(string $iconIdentifier, string $title, UriInterface $href): string
1001
    {
1002
        $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
1003
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
1004
        return (string) $buttonBar->makeLinkButton()
1005
            ->setHref((string) $href)
1006
            ->setIcon($this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL))
1007
            ->setTitle($title)
1008
            ->setShowLabelText(true);
1009
    }
1010
1011
    /**
1012
     * Returns the URL to the current module, including $_GET['id'].
1013
     *
1014
     * @param array $uriParameters optional parameters to add to the URL
1015
     *
1016
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
1017
     */
1018
    protected function getInfoModuleUrl(array $uriParameters = []): Uri
1019
    {
1020
        if (GeneralUtility::_GP('id')) {
1021
            $uriParameters = array_merge($uriParameters, [
1022
                'id' => GeneralUtility::_GP('id'),
1023
            ]);
1024
        }
1025
        /** @var UriBuilder $uriBuilder */
1026
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
1027
        return $uriBuilder->buildUriFromRoute('web_info', $uriParameters);
1028
    }
1029
1030
    private function getDepthDropDownHtml(): string
1031
    {
1032
        return BackendUtility::getFuncMenu(
1033
            $this->id,
1034
            'SET[depth]',
1035
            $this->pObj->MOD_SETTINGS['depth'],
1036
            $this->pObj->MOD_MENU['depth']
1037
        );
1038
    }
1039
1040
    private function getDisplayLogFilterHtml(int $setId): string
1041
    {
1042
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.display') . ': ' . BackendUtility::getFuncMenu(
1043
                $this->id,
1044
                'SET[log_display]',
1045
                $this->pObj->MOD_SETTINGS['log_display'],
1046
                $this->pObj->MOD_MENU['log_display'],
1047
                'index.php',
1048
                '&setID=' . $setId
1049
            );
1050
    }
1051
1052
    private function getShowResultLogCheckBoxHtml(int $setId, string $quiPart): string
1053
    {
1054
        return BackendUtility::getFuncCheck(
1055
                $this->id,
1056
                'SET[log_resultLog]',
1057
                $this->pObj->MOD_SETTINGS['log_resultLog'],
1058
                'index.php',
1059
                '&setID=' . $setId . $quiPart
1060
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showresultlog');
1061
    }
1062
1063
    private function getShowFeVarsCheckBoxHtml(int $setId, string $quiPart): string
1064
    {
1065
        return BackendUtility::getFuncCheck(
1066
                $this->id,
1067
                'SET[log_feVars]',
1068
                $this->pObj->MOD_SETTINGS['log_feVars'],
1069
                'index.php',
1070
                '&setID=' . $setId . $quiPart
1071
            ) . '&nbsp;' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.showfevars');
1072
    }
1073
1074
    private function getItemsPerPageDropDownHtml(): string
1075
    {
1076
        return $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.itemsPerPage') . ': ' .
1077
            BackendUtility::getFuncMenu(
1078
                $this->id,
1079
                'SET[itemsPerPage]',
1080
                $this->pObj->MOD_SETTINGS['itemsPerPage'],
1081
                $this->pObj->MOD_MENU['itemsPerPage']
1082
            );
1083
    }
1084
}
1085