Passed
Pull Request — master (#647)
by Tomas Norre
21:43 queued 18:06
created

MultiProcessRequestForm   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 120
c 2
b 1
f 0
dl 0
loc 262
ccs 0
cts 168
cp 0
rs 10
wmc 29

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getLinkButton() 0 9 1
A getEnableDisableLink() 0 13 2
A getAddLink() 0 13 3
A runRefreshHooks() 0 8 3
A render() 0 3 1
A getRefreshLink() 0 6 1
A handleProcessOverviewActions() 0 18 5
A getLanguageService() 0 3 1
A processOverviewAction() 0 43 4
A isPhpForkAvailable() 0 3 1
A makeCrawlerProcessableChecks() 0 17 3
A getModeLink() 0 16 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Backend\RequestForm;
6
7
/*
8
 * (c) 2020 AOE GmbH <[email protected]>
9
 *
10
 * This file is part of the TYPO3 Crawler Extension.
11
 *
12
 * It is free software; you can redistribute it and/or modify it under
13
 * the terms of the GNU General Public License, either version 2
14
 * of the License, or any later version.
15
 *
16
 * For the full copyright and license information, please read the
17
 * LICENSE.txt file that was distributed with this source code.
18
 *
19
 * The TYPO3 project - inspiring people to share!
20
 */
21
22
use AOE\Crawler\Backend\Helper\UrlBuilder;
23
use AOE\Crawler\Controller\CrawlerController;
24
use AOE\Crawler\Domain\Repository\ProcessRepository;
25
use AOE\Crawler\Domain\Repository\QueueRepository;
26
use AOE\Crawler\Exception\ProcessException;
27
use AOE\Crawler\Service\ProcessService;
28
use AOE\Crawler\Utility\MessageUtility;
29
use AOE\Crawler\Utility\PhpBinaryUtility;
30
use Psr\Http\Message\UriInterface;
31
use TYPO3\CMS\Backend\Template\ModuleTemplate;
32
use TYPO3\CMS\Core\Imaging\Icon;
33
use TYPO3\CMS\Core\Imaging\IconFactory;
34
use TYPO3\CMS\Core\Localization\LanguageService;
35
use TYPO3\CMS\Core\Utility\CommandUtility;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Core\Utility\MathUtility;
38
use TYPO3\CMS\Fluid\View\StandaloneView;
39
use TYPO3\CMS\Info\Controller\InfoModuleController;
40
41
final class MultiProcessRequestForm extends AbstractRequestForm implements RequestFormInterface
42
{
43
    /**
44
     * @var StandaloneView
45
     */
46
    private $view;
47
48
    /**
49
     * @var ProcessRepository
50
     */
51
    private $processRepository;
52
53
    /**
54
     * @var QueueRepository
55
     */
56
    private $queueRepository;
57
58
    /**
59
     * @var ProcessService
60
     */
61
    private $processService;
62
63
    /**
64
     * @var IconFactory
65
     */
66
    private $iconFactory;
67
68
    /**
69
     * @var string
70
     */
71
    private $processListMode;
0 ignored issues
show
introduced by
The private property $processListMode is not used, and could be removed.
Loading history...
72
73
    /**
74
     * @var InfoModuleController
75
     */
76
    private $infoModuleController;
77
78
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController)
79
    {
80
        $this->view = $view;
81
        $this->processRepository = new ProcessRepository();
82
        $this->queueRepository = new QueueRepository();
83
        $this->processService = GeneralUtility::makeInstance(ProcessService::class);
84
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
85
        $this->infoModuleController = $infoModuleController;
86
    }
87
88
    public function render($id, string $elementName, array $menuItems): string
89
    {
90
        return $this->processOverviewAction();
91
    }
92
93
    /**
94
     * This method is used to show an overview about the active an the finished crawling processes
95
     *
96
     * @return string
97
     */
98
    protected function processOverviewAction()
99
    {
100
        $this->view->setTemplate('ProcessOverview');
101
        $this->runRefreshHooks();
102
        $this->makeCrawlerProcessableChecks();
103
104
        try {
105
            $this->handleProcessOverviewActions();
106
        } catch (\Throwable $e) {
107
            $this->isErrorDetected = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isErrorDetected does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
108
            MessageUtility::addErrorMessage($e->getMessage());
109
        }
110
111
        $processRepository = GeneralUtility::makeInstance(ProcessRepository::class);
112
        $queueRepository = GeneralUtility::makeInstance(QueueRepository::class);
113
114
        $mode = GeneralUtility::_GP('processListMode') ?? $this->infoModuleController->MOD_SETTINGS['processListMode'];
115
        if ($mode === 'simple') {
116
            $allProcesses = $processRepository->findAllActive();
117
        } else {
118
            $allProcesses = $processRepository->findAll();
119
        }
120
        $isCrawlerEnabled = ! $this->findCrawler()->getDisabled() && ! $this->isErrorDetected;
121
        $currentActiveProcesses = $processRepository->findAllActive()->count();
122
        $maxActiveProcesses = MathUtility::forceIntegerInRange($this->extensionSettings['processLimit'], 1, 99, 1);
0 ignored issues
show
Bug Best Practice introduced by
The property extensionSettings does not exist on AOE\Crawler\Backend\Requ...MultiProcessRequestForm. Did you maybe forget to declare it?
Loading history...
123
        $this->view->assignMultiple([
124
            'pageId' => (int) $this->id,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on AOE\Crawler\Backend\Requ...MultiProcessRequestForm. Did you maybe forget to declare it?
Loading history...
125
            'refreshLink' => $this->getRefreshLink(),
126
            'addLink' => $this->getAddLink($currentActiveProcesses, $maxActiveProcesses, $isCrawlerEnabled),
127
            'modeLink' => $this->getModeLink($mode),
128
            'enableDisableToggle' => $this->getEnableDisableLink($isCrawlerEnabled),
129
            'processCollection' => $allProcesses,
130
            'cliPath' => $this->processService->getCrawlerCliPath(),
131
            'isCrawlerEnabled' => $isCrawlerEnabled,
132
            'totalUnprocessedItemCount' => $queueRepository->countAllPendingItems(),
133
            'assignedUnprocessedItemCount' => $queueRepository->countAllAssignedPendingItems(),
134
            'activeProcessCount' => $currentActiveProcesses,
135
            'maxActiveProcessCount' => $maxActiveProcesses,
136
            'mode' => $mode,
137
            'displayActions' => 0,
138
        ]);
139
140
        return $this->view->render();
141
    }
142
143
    /**
144
     * Verify that the crawler is executable.
145
     */
146
    private function makeCrawlerProcessableChecks(): void
147
    {
148
        if (! $this->isPhpForkAvailable()) {
149
            $this->isErrorDetected = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isErrorDetected does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
150
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.noPhpForkAvailable'));
151
        }
152
153
        $exitCode = 0;
154
        $out = [];
155
        CommandUtility::exec(
156
            PhpBinaryUtility::getPhpBinary() . ' -v',
157
            $out,
158
            $exitCode
159
        );
160
        if ($exitCode > 0) {
161
            $this->isErrorDetected = true;
162
            MessageUtility::addErrorMessage(sprintf($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.phpBinaryNotFound'), htmlspecialchars($this->extensionSettings['phpPath'])));
0 ignored issues
show
Bug Best Practice introduced by
The property extensionSettings does not exist on AOE\Crawler\Backend\Requ...MultiProcessRequestForm. Did you maybe forget to declare it?
Loading history...
163
        }
164
    }
165
166
    /**
167
     * Indicate that the required PHP method "popen" is
168
     * available in the system.
169
     */
170
    private function isPhpForkAvailable(): bool
171
    {
172
        return function_exists('popen');
173
    }
174
175
    private function getLanguageService(): LanguageService
176
    {
177
        return $GLOBALS['LANG'];
178
    }
179
180
    private function getLinkButton(string $iconIdentifier, string $title, UriInterface $href): string
181
    {
182
        $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
183
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
184
        return (string) $buttonBar->makeLinkButton()
185
            ->setHref((string) $href)
186
            ->setIcon($this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL))
187
            ->setTitle($title)
188
            ->setShowLabelText(true);
189
    }
190
191
    /**
192
     * Method to handle incomming actions of the process overview
193
     *
194
     * @throws ProcessException
195
     */
196
    private function handleProcessOverviewActions(): void
197
    {
198
        switch (GeneralUtility::_GP('action')) {
199
            case 'stopCrawling':
200
                //set the cli status to disable (all processes will be terminated)
201
                $this->findCrawler()->setDisabled(true);
202
                break;
203
            case 'addProcess':
204
                if ($this->processService->startProcess() === false) {
0 ignored issues
show
introduced by
The condition $this->processService->startProcess() === false is always false.
Loading history...
205
                    throw new ProcessException($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocesserror'));
206
                }
207
                MessageUtility::addNoticeMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocess'));
208
                break;
209
            case 'resumeCrawling':
210
            default:
211
                //set the cli status to end (all processes will be terminated)
212
                $this->findCrawler()->setDisabled(false);
213
                break;
214
        }
215
    }
216
217
    /**
218
     * Returns a tag for the refresh icon
219
     *
220
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
221
     */
222
    private function getRefreshLink(): string
223
    {
224
        return $this->getLinkButton(
225
            'actions-refresh',
226
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.refresh'),
227
            UrlBuilder::getInfoModuleUrl(['SET[\'crawleraction\']' => 'crawleraction', 'id' => $this->id])
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on AOE\Crawler\Backend\Requ...MultiProcessRequestForm. Did you maybe forget to declare it?
Loading history...
228
        );
229
    }
230
231
    /**
232
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
233
     */
234
    private function getAddLink(int $currentActiveProcesses, int $maxActiveProcesses, bool $isCrawlerEnabled): string
235
    {
236
        if (! $isCrawlerEnabled) {
237
            return '';
238
        }
239
        if ($currentActiveProcesses >= $maxActiveProcesses) {
240
            return '';
241
        }
242
243
        return $this->getLinkButton(
244
            'actions-add',
245
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.process.add'),
246
            UrlBuilder::getInfoModuleUrl(['action' => 'addProcess'])
247
        );
248
    }
249
250
    /**
251
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
252
     */
253
    private function getModeLink(string $mode): string
254
    {
255
        if ($mode === 'detail') {
256
            return $this->getLinkButton(
257
                'actions-document-view',
258
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.running'),
259
                UrlBuilder::getInfoModuleUrl(['processListMode' => 'simple'])
260
            );
261
        } elseif ($mode === 'simple') {
262
            return $this->getLinkButton(
263
                'actions-document-view',
264
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.all'),
265
                UrlBuilder::getInfoModuleUrl(['processListMode' => 'detail'])
266
            );
267
        }
268
        return '';
269
    }
270
271
    /**
272
     * Returns a link for the panel to enable or disable the crawler
273
     *
274
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
275
     */
276
    private function getEnableDisableLink(bool $isCrawlerEnabled): string
277
    {
278
        if ($isCrawlerEnabled) {
279
            return $this->getLinkButton(
280
                'tx-crawler-stop',
281
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.disablecrawling'),
282
                UrlBuilder::getInfoModuleUrl(['action' => 'stopCrawling'])
283
            );
284
        }
285
        return $this->getLinkButton(
286
            'tx-crawler-start',
287
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.enablecrawling'),
288
            UrlBuilder::getInfoModuleUrl(['action' => 'resumeCrawling'])
289
        );
290
    }
291
292
    /**
293
     * Activate hooks
294
     */
295
    private function runRefreshHooks(): void
296
    {
297
        $crawlerLib = GeneralUtility::makeInstance(CrawlerController::class);
298
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['crawler']['refresh_hooks'] ?? [] as $objRef) {
299
            /** @var CrawlerHookInterface $hookObj */
300
            $hookObj = GeneralUtility::makeInstance($objRef);
301
            if (is_object($hookObj)) {
302
                $hookObj->crawler_init($crawlerLib);
303
            }
304
        }
305
    }
306
}
307