Passed
Push — refactor/backendModule-ValueOb... ( ab393e...9011ea )
by Tomas Norre
16:36
created

MultiProcessRequestForm::getRefreshLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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