Passed
Push — refactor/backendModule-ValueOb... ( 0f77d0...7f5344 )
by Tomas Norre
07:41
created

BackendModule   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 423
Duplicated Lines 0 %

Test Coverage

Coverage 12.5%

Importance

Changes 7
Bugs 0 Features 1
Metric Value
eloc 161
c 7
b 0
f 1
dl 0
loc 423
ccs 27
cts 216
cp 0.125
rs 9.44
wmc 37

17 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 7 1
A __construct() 0 11 1
A modMenu() 0 3 1
A getModuleMenu() 0 29 1
A generateConfigurationSelectors() 0 39 3
A main() 0 21 2
A findCrawler() 0 6 2
A renderForm() 0 7 1
A initializeView() 0 8 1
A getLanguageService() 0 3 1
A runRefreshHooks() 0 8 3
A getLinkButton() 0 9 1
A getModeLink() 0 16 3
A getEnableDisableLink() 0 13 2
A getResFeVars() 0 7 2
A getInfoModuleUrl() 0 10 2
B selectorBox() 0 14 10
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\Backend\RequestForm\RequestFormFactory;
32
use AOE\Crawler\Configuration\ExtensionConfigurationProvider;
33
use AOE\Crawler\Controller\CrawlerController;
34
use AOE\Crawler\Converter\JsonCompatibilityConverter;
35
use AOE\Crawler\Domain\Repository\QueueRepository;
36
use AOE\Crawler\Hooks\CrawlerHookInterface;
37
use AOE\Crawler\Service\ProcessService;
38
use AOE\Crawler\Value\CrawlAction;
39
use AOE\Crawler\Value\ModuleSettings;
40
use Psr\Http\Message\UriInterface;
41
use TYPO3\CMS\Backend\Routing\UriBuilder;
42
use TYPO3\CMS\Backend\Template\ModuleTemplate;
43
use TYPO3\CMS\Backend\Utility\BackendUtility;
44
use TYPO3\CMS\Core\Database\ConnectionPool;
45
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
46
use TYPO3\CMS\Core\Http\Uri;
47
use TYPO3\CMS\Core\Imaging\Icon;
48
use TYPO3\CMS\Core\Imaging\IconFactory;
49
use TYPO3\CMS\Core\Localization\LanguageService;
50
use TYPO3\CMS\Core\Utility\GeneralUtility;
51
use TYPO3\CMS\Extbase\Object\ObjectManager;
52
use TYPO3\CMS\Fluid\View\StandaloneView;
53
use TYPO3\CMS\Info\Controller\InfoModuleController;
54
55
/**
56
 * Function for Info module, containing three main actions:
57
 * - List of all queued items
58
 * - Log functionality
59
 * - Process overview
60
 */
61
class BackendModule
62
{
63
    /**
64
     * @var InfoModuleController Contains a reference to the parent calling object
65
     */
66
    protected $pObj;
67
68
    /**
69
     * The current page ID
70
     * @var int
71
     */
72
    protected $id;
73
74
    // Internal, dynamic:
75
76
    /**
77
     * @var array
78
     */
79
    protected $duplicateTrack = [];
80
81
    /**
82
     * @var bool
83
     */
84
    protected $submitCrawlUrls = false;
85
86
    /**
87
     * @var bool
88
     */
89
    protected $downloadCrawlUrls = false;
90
91
    /**
92
     * @var int
93
     */
94
    protected $scheduledTime = 0;
95
96
    /**
97
     * @var int
98
     */
99
    protected $reqMinute = 1000;
100
101
    /**
102
     * @var array holds the selection of configuration from the configuration selector box
103
     */
104
    protected $incomingConfigurationSelection = [];
105
106
    /**
107
     * @var CrawlerController
108
     */
109
    protected $crawlerController;
110
111
    /**
112
     * @var array
113
     */
114
    protected $CSVaccu = [];
115
116
    /**
117
     * If true the user requested a CSV export of the queue
118
     *
119
     * @var boolean
120
     */
121
    protected $CSVExport = false;
122
123
    /**
124
     * @var array
125
     */
126
    protected $downloadUrls = [];
127
128
    /**
129
     * Holds the configuration from ext_conf_template loaded by getExtensionConfiguration()
130
     *
131
     * @var array
132
     */
133
    protected $extensionSettings = [];
134
135
    /**
136
     * Indicate that an flash message with an error is present.
137
     *
138
     * @var boolean
139
     */
140
    protected $isErrorDetected = false;
141
142
    /**
143
     * @var ProcessService
144
     */
145
    protected $processManager;
146
147
    /**
148
     * @var QueryBuilder
149
     */
150
    protected $queryBuilder;
151
152
    /**
153
     * @var QueueRepository
154
     */
155
    protected $queueRepository;
156
157
    /**
158
     * @var StandaloneView
159
     */
160
    protected $view;
161
162
    /**
163
     * @var IconFactory
164
     */
165
    protected $iconFactory;
166
167
    /**
168
     * @var JsonCompatibilityConverter
169
     */
170
    protected $jsonCompatibilityConverter;
171
172
    /**
173
     * @var LanguageService
174
     */
175
    private $languageService;
176
177
    /**
178
     * @var ModuleSettings
179
     */
180
    private $moduleSettings;
0 ignored issues
show
introduced by
The private property $moduleSettings is not used, and could be removed.
Loading history...
181
182
    public function __construct()
183
    {
184
        $this->languageService = $GLOBALS['LANG'];
185
        $objectManger = GeneralUtility::makeInstance(ObjectManager::class);
186
        $this->processManager = $objectManger->get(ProcessService::class);
187
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_crawler_queue');
188
        $this->queueRepository = $objectManger->get(QueueRepository::class);
189
        $this->initializeView();
190
        $this->extensionSettings = GeneralUtility::makeInstance(ExtensionConfigurationProvider::class)->getExtensionConfiguration();
191
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
192
        $this->jsonCompatibilityConverter = GeneralUtility::makeInstance(JsonCompatibilityConverter::class);
193
    }
194
195
    /**
196
     * Called by the InfoModuleController
197
     */
198
    public function init(InfoModuleController $pObj): void
199
    {
200
201
        $this->pObj = $pObj;
202
        $this->id = (int) GeneralUtility::_GP('id');
203
        // Setting MOD_MENU items as we need them for logging:
204
        $this->pObj->MOD_MENU = array_merge($this->pObj->MOD_MENU, $this->getModuleMenu());
205
    }
206
207 1
    private function getModuleMenu(): 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
    /**
241
     * Additions to the function menu array
242
     *
243
     * @return array Menu array
244
     * @deprecated Using BackendModule->modMenu() is deprecated since 9.1.1 and will be removed in v11.x
245
     */
246 1
    public function modMenu(): array
247
    {
248 1
        return $this->getModuleMenu();
249
    }
250
251
    public function main(): string
252
    {
253
        if (empty($this->pObj->MOD_SETTINGS['processListMode'])) {
254
            $this->pObj->MOD_SETTINGS['processListMode'] = 'simple';
255
        }
256
        $this->view->assign('currentPageId', $this->id);
257
258
        $selectedAction = new CrawlAction($this->pObj->MOD_SETTINGS['crawlaction'] ?? 'start');
259
260
        // Type function menu:
261
        $actionDropdown = BackendUtility::getFuncMenu(
262
            $this->id,
263
            'SET[crawlaction]',
264
            $selectedAction,
265
            $this->pObj->MOD_MENU['crawlaction']
266
        );
267
268
        $theOutput = '<h2>' . htmlspecialchars($this->getLanguageService()->getLL('title')) . '</h2>' . $actionDropdown;
269
        $theOutput .= $this->renderForm($selectedAction);
270
271
        return $theOutput;
272
    }
273
274
275
276
    /**
277
     * Generates the configuration selectors for compiling URLs:
278
     */
279
    protected function generateConfigurationSelectors(): array
280
    {
281
        $selectors = [];
282
        $selectors['depth'] = $this->selectorBox(
283
            [
284
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
285
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
286
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
287
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
288
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
289
                99 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
290
            ],
291
            'SET[depth]',
292
            $this->pObj->MOD_SETTINGS['depth'],
293
            false
294
        );
295
296
        // Configurations
297
        $availableConfigurations = $this->crawlerController->getConfigurationsForBranch((int) $this->id, (int) $this->pObj->MOD_SETTINGS['depth'] ?: 0);
298
        $selectors['configurations'] = $this->selectorBox(
299
            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

299
            /** @scrutinizer ignore-type */ empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
Loading history...
300
            'configurationSelection',
301
            $this->incomingConfigurationSelection,
302
            true
303
        );
304
305
        // Scheduled time:
306
        $selectors['scheduled'] = $this->selectorBox(
307
            [
308
                'now' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.now'),
309
                'midnight' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.midnight'),
310
                '04:00' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.4am'),
311
            ],
312
            'tstamp',
313
            GeneralUtility::_POST('tstamp'),
314
            false
315
        );
316
317
        return $selectors;
318
    }
319
320
    /**
321
     * Find Fe vars
322
     */
323 4
    protected function getResFeVars(array $resultData): array
324
    {
325 4
        if (empty($resultData)) {
326 1
            return [];
327
        }
328 3
        $requestResult = $this->jsonCompatibilityConverter->convert($resultData['content']);
329 3
        return $requestResult['vars'] ?? [];
330
    }
331
332
    /**
333
     * Returns a link for the panel to enable or disable the crawler
334
     *
335
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
336
     */
337
    protected function getEnableDisableLink(bool $isCrawlerEnabled): string
338
    {
339
        if ($isCrawlerEnabled) {
340
            return $this->getLinkButton(
341
                'tx-crawler-stop',
342
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.disablecrawling'),
343
                $this->getInfoModuleUrl(['action' => 'stopCrawling'])
344
            );
345
        }
346
        return $this->getLinkButton(
347
            'tx-crawler-start',
348
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.enablecrawling'),
349
            $this->getInfoModuleUrl(['action' => 'resumeCrawling'])
350
        );
351
    }
352
353
    /**
354
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
355
     */
356
    protected function getModeLink(string $mode): string
357
    {
358
        if ($mode === 'detail') {
359
            return $this->getLinkButton(
360
                'actions-document-view',
361
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.running'),
362
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'simple'])
363
            );
364
        } elseif ($mode === 'simple') {
365
            return $this->getLinkButton(
366
                'actions-document-view',
367
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.all'),
368
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'detail'])
369
            );
370
        }
371
        return '';
372
    }
373
374
    /**
375
     * Returns the singleton instance of the crawler.
376
     */
377
    protected function findCrawler(): CrawlerController
378
    {
379
        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...
380
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
381
        }
382
        return $this->crawlerController;
383
    }
384
385
    /*****************************
386
     *
387
     * General Helper Functions
388
     *
389
     *****************************/
390
391
    /**
392
     * Create selector box
393
     *
394
     * @param array $optArray Options key(value) => label pairs
395
     * @param string $name Selector box name
396
     * @param string|array $value Selector box value (array for multiple...)
397
     * @param boolean $multiple If set, will draw multiple box.
398
     *
399
     * @return string HTML select element
400
     */
401
    protected function selectorBox($optArray, $name, $value, bool $multiple): string
402
    {
403
        if (! is_string($value) || ! is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
404
            $value = '';
405
        }
406
407
        $options = [];
408
        foreach ($optArray as $key => $val) {
409
            $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...
410
            $options[] = '
411
                <option value="' . $key . '" ' . ($selected ? ' selected="selected"' : '') . '>' . htmlspecialchars($val) . '</option>';
412
        }
413
414
        return '<select class="form-control" name="' . htmlspecialchars($name . ($multiple ? '[]' : '')) . '"' . ($multiple ? ' multiple' : '') . '>' . implode('', $options) . '</select>';
415
    }
416
417
    /**
418
     * Activate hooks
419
     */
420
    protected function runRefreshHooks(): void
421
    {
422
        $crawlerLib = GeneralUtility::makeInstance(CrawlerController::class);
423
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['crawler']['refresh_hooks'] ?? [] as $objRef) {
424
            /** @var CrawlerHookInterface $hookObj */
425
            $hookObj = GeneralUtility::makeInstance($objRef);
426
            if (is_object($hookObj)) {
427
                $hookObj->crawler_init($crawlerLib);
428
            }
429
        }
430
    }
431
432
    protected function initializeView(): void
433
    {
434
        $view = GeneralUtility::makeInstance(StandaloneView::class);
435
        $view->setLayoutRootPaths(['EXT:crawler/Resources/Private/Layouts']);
436
        $view->setPartialRootPaths(['EXT:crawler/Resources/Private/Partials']);
437
        $view->setTemplateRootPaths(['EXT:crawler/Resources/Private/Templates/Backend']);
438
        $view->getRequest()->setControllerExtensionName('Crawler');
439
        $this->view = $view;
440
    }
441
442
    protected function getLanguageService(): LanguageService
443
    {
444
        return $GLOBALS['LANG'];
445
    }
446
447
    protected function getLinkButton(string $iconIdentifier, string $title, UriInterface $href): string
448
    {
449
        $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
450
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
451
        return (string) $buttonBar->makeLinkButton()
452
            ->setHref((string) $href)
453
            ->setIcon($this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL))
454
            ->setTitle($title)
455
            ->setShowLabelText(true);
456
    }
457
458
    /**
459
     * Returns the URL to the current module, including $_GET['id'].
460
     *
461
     * @param array $uriParameters optional parameters to add to the URL
462
     *
463
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
464
     */
465
    protected function getInfoModuleUrl(array $uriParameters = []): Uri
466
    {
467
        if (GeneralUtility::_GP('id')) {
468
            $uriParameters = array_merge($uriParameters, [
469
                'id' => GeneralUtility::_GP('id'),
470
            ]);
471
        }
472
        /** @var UriBuilder $uriBuilder */
473
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
474
        return $uriBuilder->buildUriFromRoute('web_info', $uriParameters);
475
    }
476
477
    private function renderForm(CrawlAction $selectedAction): string
478
    {
479
        $requestForm = RequestFormFactory::create($selectedAction, $this->view);
480
        return $requestForm->render(
481
            $this->id,
482
            $this->pObj->MOD_SETTINGS['depth'],
483
            $this->pObj->MOD_MENU['depth']
484
        );
485
    }
486
}
487