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

MultiProcessRequestForm::getInfoModuleUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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