Passed
Push — refactor/backendModule-ValueOb... ( 355cec...0f77d0 )
by Tomas Norre
13:40
created

MultiProcessRequestForm::__construct()   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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 8
ccs 0
cts 8
cp 0
crap 2
rs 10
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\Controller\CrawlerController;
23
use AOE\Crawler\Domain\Repository\ProcessRepository;
24
use AOE\Crawler\Domain\Repository\QueueRepository;
25
use AOE\Crawler\Exception\ProcessException;
26
use AOE\Crawler\Service\ProcessService;
27
use AOE\Crawler\Utility\MessageUtility;
28
use AOE\Crawler\Utility\PhpBinaryUtility;
29
use Psr\Http\Message\UriInterface;
30
use TYPO3\CMS\Backend\Routing\UriBuilder;
31
use TYPO3\CMS\Backend\Template\ModuleTemplate;
32
use TYPO3\CMS\Core\Http\Uri;
33
use TYPO3\CMS\Core\Imaging\Icon;
34
use TYPO3\CMS\Core\Imaging\IconFactory;
35
use TYPO3\CMS\Core\Localization\LanguageService;
36
use TYPO3\CMS\Core\Utility\CommandUtility;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Core\Utility\MathUtility;
39
use TYPO3\CMS\Fluid\View\StandaloneView;
40
41
final class MultiProcessRequestForm implements RequestForm
42
{
43
    /** @var StandaloneView */
44
    private $view;
45
46
    /** @var ProcessRepository */
47
    private $processRepository;
48
49
    /** @var QueueRepository */
50
    private $queueRepository;
51
52
    /** @var CrawlerController */
53
    private $crawlerController;
54
55
    /** @var ProcessService */
56
    private $processService;
57
58
    /** @var IconFactory  */
59
    private $iconFactory;
60
61
    public function __construct(StandaloneView $view)
62
    {
63
        $this->view = $view;
64
        $this->processRepository = new ProcessRepository();
65
        $this->queueRepository = new QueueRepository();
66
        $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
67
        $this->processService = GeneralUtility::makeInstance(ProcessService::class);
68
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
69
    }
70
71
    public function render($id, string $elementName, array $menuItems): string
72
    {
73
        return $this->processOverviewAction();
74
    }
75
76
    /**
77
     * This method is used to show an overview about the active an the finished crawling processes
78
     *
79
     * @return string
80
     */
81
    private function processOverviewAction(): string
82
    {
83
        $this->view->setTemplate('ProcessOverview');
84
85
        // Add back RefreshHooks
86
        //$this->runRefreshHooks();
87
        $this->makeCrawlerProcessableChecks();
88
89
        try {
90
            $this->handleProcessOverviewActions();
91
        } catch (\Throwable $e) {
92
            $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...
93
            MessageUtility::addErrorMessage($e->getMessage());
94
        }
95
96
        $processRepository = new ProcessRepository();
97
        $queueRepository = new QueueRepository();
98
99
        $mode = $this->pObj->MOD_SETTINGS['processListMode'] ?? 'simple';
0 ignored issues
show
Bug Best Practice introduced by
The property pObj does not exist on AOE\Crawler\Backend\Requ...MultiProcessRequestForm. Did you maybe forget to declare it?
Loading history...
100
        if ($mode === 'simple') {
101
            $allProcesses = $processRepository->findAllActive();
102
        } else {
103
            $allProcesses = $processRepository->findAll();
104
        }
105
        $isCrawlerEnabled = ! $this->crawlerController->getDisabled() && ! $this->isErrorDetected;
106
        $currentActiveProcesses = $processRepository->countActive();
107
        $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...
108
        $this->view->assignMultiple([
109
            '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...
110
            'refreshLink' => $this->getRefreshLink(),
111
            'addLink' => $this->getAddLink($currentActiveProcesses, $maxActiveProcesses, $isCrawlerEnabled),
112
            'modeLink' => $this->getModeLink($mode),
113
            'enableDisableToggle' => $this->getEnableDisableLink($isCrawlerEnabled),
114
            'processCollection' => $allProcesses,
115
            'cliPath' => $this->processService->getCrawlerCliPath(),
116
            'isCrawlerEnabled' => $isCrawlerEnabled,
117
            'totalUnprocessedItemCount' => $queueRepository->countAllPendingItems(),
118
            'assignedUnprocessedItemCount' => $queueRepository->countAllAssignedPendingItems(),
119
            'activeProcessCount' => $currentActiveProcesses,
120
            'maxActiveProcessCount' => $maxActiveProcesses,
121
            'mode' => $mode,
122
            'displayActions' => 0,
123
        ]);
124
125
        return $this->view->render();
126
    }
127
128
    /**
129
     * Verify that the crawler is executable.
130
     */
131
    private function makeCrawlerProcessableChecks(): void
132
    {
133
        if (! $this->isPhpForkAvailable()) {
134
            $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...
135
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.noPhpForkAvailable'));
136
        }
137
138
        $exitCode = 0;
139
        $out = [];
140
        CommandUtility::exec(
141
            PhpBinaryUtility::getPhpBinary() . ' -v',
142
            $out,
143
            $exitCode
144
        );
145
        if ($exitCode > 0) {
146
            $this->isErrorDetected = true;
147
            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...
148
        }
149
    }
150
151
    /**
152
     * Indicate that the required PHP method "popen" is
153
     * available in the system.
154
     */
155
    private function isPhpForkAvailable(): bool
156
    {
157
        return function_exists('popen');
158
    }
159
160
    private function getLanguageService(): LanguageService
161
    {
162
        return $GLOBALS['LANG'];
163
    }
164
165
    private function getLinkButton(string $iconIdentifier, string $title, UriInterface $href): string
166
    {
167
        $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
168
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
169
        return (string) $buttonBar->makeLinkButton()
170
            ->setHref((string) $href)
171
            ->setIcon($this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL))
172
            ->setTitle($title)
173
            ->setShowLabelText(true);
174
    }
175
176
    /**
177
     * Returns the URL to the current module, including $_GET['id'].
178
     *
179
     * @param array $uriParameters optional parameters to add to the URL
180
     *
181
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
182
     * TODO: Move to UrlService?
183
     */
184
    private function getInfoModuleUrl(array $uriParameters = []): Uri
185
    {
186
        if (GeneralUtility::_GP('id')) {
187
            $uriParameters = array_merge($uriParameters, [
188
                'id' => GeneralUtility::_GP('id'),
189
            ]);
190
        }
191
        /** @var UriBuilder $uriBuilder */
192
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
193
        return $uriBuilder->buildUriFromRoute('web_info', $uriParameters);
194
    }
195
196
    /**
197
     * Method to handle incomming actions of the process overview
198
     *
199
     * @throws ProcessException
200
     */
201
    private function handleProcessOverviewActions(): void
202
    {
203
        switch (GeneralUtility::_GP('action')) {
204
            case 'stopCrawling':
205
                //set the cli status to disable (all processes will be terminated)
206
                $this->crawlerController->setDisabled(true);
207
                break;
208
            case 'addProcess':
209
                if ($this->processService->startProcess() === false) {
0 ignored issues
show
introduced by
The condition $this->processService->startProcess() === false is always false.
Loading history...
210
                    throw new ProcessException($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocesserror'));
211
                }
212
                MessageUtility::addNoticeMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.newprocess'));
213
                break;
214
            case 'resumeCrawling':
215
            default:
216
                //set the cli status to end (all processes will be terminated)
217
                $this->crawlerController->setDisabled(false);
218
                break;
219
        }
220
    }
221
222
    /**
223
     * Returns a tag for the refresh icon
224
     *
225
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
226
     */
227
    private function getRefreshLink(): string
228
    {
229
        return $this->getLinkButton(
230
            'actions-refresh',
231
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.refresh'),
232
            $this->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...
233
        );
234
    }
235
236
    /**
237
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
238
     */
239
    private function getAddLink(int $currentActiveProcesses, int $maxActiveProcesses, bool $isCrawlerEnabled): string
240
    {
241
        if (! $isCrawlerEnabled) {
242
            return '';
243
        }
244
        if ($currentActiveProcesses >= $maxActiveProcesses) {
245
            return '';
246
        }
247
248
        return $this->getLinkButton(
249
            'actions-add',
250
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.process.add'),
251
            $this->getInfoModuleUrl(['action' => 'addProcess'])
252
        );
253
    }
254
255
    /**
256
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
257
     */
258
    private function getModeLink(string $mode): string
259
    {
260
        if ($mode === 'detail') {
261
            return $this->getLinkButton(
262
                'actions-document-view',
263
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.running'),
264
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'simple'])
265
            );
266
        } elseif ($mode === 'simple') {
267
            return $this->getLinkButton(
268
                'actions-document-view',
269
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.show.all'),
270
                $this->getInfoModuleUrl(['SET[\'processListMode\']' => 'detail'])
271
            );
272
        }
273
        return '';
274
    }
275
276
    /**
277
     * Returns a link for the panel to enable or disable the crawler
278
     *
279
     * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
280
     */
281
    private function getEnableDisableLink(bool $isCrawlerEnabled): string
282
    {
283
        if ($isCrawlerEnabled) {
284
            return $this->getLinkButton(
285
                'tx-crawler-stop',
286
                $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.disablecrawling'),
287
                $this->getInfoModuleUrl(['action' => 'stopCrawling'])
288
            );
289
        }
290
        return $this->getLinkButton(
291
            'tx-crawler-start',
292
            $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.enablecrawling'),
293
            $this->getInfoModuleUrl(['action' => 'resumeCrawling'])
294
        );
295
    }
296
297
}
298