Passed
Push — features/addFlushedPagesToCraw... ( dc2b3a...98a0d9 )
by Tomas Norre
08:44
created

BackendModule::getItemsPerPageDropDownHtml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 8
ccs 0
cts 8
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
                $trClass = '';
647
                $warningIcon = '';
648
                if ($rowData['exec_time'] !== 0 && $resultData === false) {
649
                    $trClass = 'class="bg-danger"';
650
                    $warningIcon = $this->iconFactory->getIcon('actions-ban', Icon::SIZE_SMALL);
651
                }
652
653
                // Put rows together:
654
                $content .= '
655
                    <tr ' . $trClass . ' >
656
                        ' . $titleClm . '
657
                        <td><a href="' . $this->getInfoModuleUrl(['qid_details' => $vv['qid'], 'setID' => $setId]) . '">' . htmlspecialchars((string) $vv['qid']) . '</a></td>
658
                        <td><a href="' . $this->getInfoModuleUrl(['qid_read' => $vv['qid'], 'setID' => $setId]) . '">' . $refreshIcon . '</a>&nbsp;&nbsp;' . $warningIcon . '</td>';
659
                foreach ($rowData as $fKey => $value) {
660
                    if ($fKey === 'url') {
661
                        $content .= '<td>' . $value . '</td>';
662
                    } else {
663
                        $content .= '<td>' . nl2br(htmlspecialchars(strval($value))) . '</td>';
664
                    }
665
                }
666
                $content .= '</tr>';
667
                $c++;
668
669
                if ($this->CSVExport) {
670
                    // Only for CSV (adding qid and scheduled/exec_time if needed):
671
                    $rowData['result_log'] = implode('// ', explode(chr(10), $resLog));
672
                    $rowData['qid'] = $vv['qid'];
673
                    $rowData['scheduled'] = BackendUtility::datetime($vv['scheduled']);
674
                    $rowData['exec_time'] = $vv['exec_time'] ? BackendUtility::datetime($vv['exec_time']) : '-';
675
                    $this->CSVaccu[] = $rowData;
676
                }
677
            }
678
        } else {
679
            // Compile row:
680
            $content = '
681
                <tr>
682
                    <td>' . $titleString . '</td>
683
                    <td colspan="' . $colSpan . '"><em>' . $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noentries') . '</em></td>
684
                </tr>';
685
        }
686
687
        return $content;
688
    }
689
690
    /**
691
     * Find Fe vars
692
     */
693 4
    protected function getResFeVars(array $resultData): array
694
    {
695 4
        if (empty($resultData)) {
696 1
            return [];
697
        }
698 3
        $requestResult = $this->jsonCompatibilityConverter->convert($resultData['content']);
699 3
        return $requestResult['vars'] ?? [];
700
    }
701
702
    /**
703
     * Extract the log information from the current row and retrieve it as formatted string.
704
     *
705
     * @param array $resultRow
706
     * @return string
707
     */
708 6
    protected function getResultLog($resultRow)
709
    {
710 6
        $content = '';
711 6
        if (is_array($resultRow) && array_key_exists('result_data', $resultRow)) {
712 5
            $requestContent = $this->jsonCompatibilityConverter->convert($resultRow['result_data']) ?: ['content' => ''];
713 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

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