Passed
Push — release-11.5.x ( 68ae44...542dd4 )
by Markus
33:42
created

AbstractModuleController::initializeView()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 24
ccs 0
cts 15
cp 0
rs 9.4888
c 0
b 0
f 0
cc 5
nc 8
nop 1
crap 30
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace ApacheSolrForTypo3\Solr\Controller\Backend\Search;
17
18
use ApacheSolrForTypo3\Solr\ConnectionManager;
19
use ApacheSolrForTypo3\Solr\Domain\Site\Site;
20
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
21
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
22
use ApacheSolrForTypo3\Solr\System\Mvc\Backend\Service\ModuleDataStorageService;
23
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection as SolrCoreConnection;
24
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
25
use InvalidArgumentException;
26
use Psr\Http\Message\ResponseInterface;
27
use Throwable;
28
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
29
use TYPO3\CMS\Backend\Template\ModuleTemplate;
30
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
31
use TYPO3\CMS\Backend\Utility\BackendUtility;
32
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
33
use TYPO3\CMS\Core\Http\RedirectResponse;
34
use TYPO3\CMS\Core\Messaging\AbstractMessage;
35
use TYPO3\CMS\Core\Site\SiteFinder;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
38
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException;
39
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
40
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
41
use TYPO3Fluid\Fluid\View\ViewInterface;
42
43
/**
44
 * Abstract Module
45
 */
46
abstract class AbstractModuleController extends ActionController
47
{
48
    /**
49
     * In the page-tree selected page UID
50
     *
51
     * @var int
52
     */
53
    protected int $selectedPageUID;
54
55
    /**
56
     * Holds the requested page UID because the selected page uid,
57
     * might be overwritten by the automatic site selection.
58
     *
59
     * @var int
60
     */
61
    protected int $requestedPageUID;
62
63
    /**
64
     * @var ?Site
65
     */
66
    protected ?Site $selectedSite = null;
67
68
    /**
69
     * @var SiteRepository
70
     */
71
    protected SiteRepository $siteRepository;
72
73
    /**
74
     * @var SolrCoreConnection|null
75
     */
76
    protected ?SolrCoreConnection $selectedSolrCoreConnection = null;
77
78
    /**
79
     * @var Menu|null
80
     */
81
    protected ?Menu $coreSelectorMenu = null;
82
83
    /**
84
     * @var ConnectionManager
85
     */
86
    protected ConnectionManager $solrConnectionManager;
87
88
    /**
89
     * @var ModuleDataStorageService
90
     */
91
    protected ModuleDataStorageService $moduleDataStorageService;
92
93
    /**
94
     * @var Queue
95
     */
96
    protected Queue $indexQueue;
97
98
    /**
99
     * @var SiteFinder
100
     */
101
    protected SiteFinder $siteFinder;
102
103
    /**
104
     * @var ModuleTemplateFactory
105
     */
106
    protected ModuleTemplateFactory $moduleTemplateFactory;
107
108
    /**
109
     * @var ModuleTemplate
110
     */
111
    protected ModuleTemplate $moduleTemplate;
112
113
    /**
114
     * Constructor for dependency injection
115
     */
116 5
    public function __construct(
117
        ModuleTemplateFactory $moduleTemplateFactory,
118
        ModuleDataStorageService $moduleDataStorageService,
119
        SiteRepository $siteRepository,
120
        SiteFinder $siteFinder,
121
        ConnectionManager $solrConnectionManager,
122
        Queue $indexQueue,
123
        ?int $selectedPageUID = null
124
    ) {
125 5
        $this->moduleTemplateFactory = $moduleTemplateFactory;
126 5
        $this->moduleDataStorageService = $moduleDataStorageService;
127 5
        $this->siteRepository = $siteRepository;
128 5
        $this->siteFinder = $siteFinder;
129 5
        $this->solrConnectionManager = $solrConnectionManager;
130 5
        $this->indexQueue = $indexQueue;
131 5
        $this->selectedPageUID = $selectedPageUID ?? (int)GeneralUtility::_GP('id');
132
    }
133
134
    /**
135
     * Injects UriBuilder object.
136
     *
137
     * Purpose: Is already set in {@link processRequest} but wanted in PhpUnit
138
     *
139
     * @param UriBuilder $uriBuilder
140
     */
141 5
    public function injectUriBuilder(UriBuilder $uriBuilder)
142
    {
143 5
        $this->uriBuilder = $uriBuilder;
144
    }
145
146
    /**
147
     * @param Site $selectedSite
148
     */
149 5
    public function setSelectedSite(Site $selectedSite)
150
    {
151 5
        $this->selectedSite = $selectedSite;
152
    }
153
154
    /**
155
     * Initializes the controller and sets needed vars.
156
     * @throws DBALDriverException
157
     * @throws Throwable
158
     * @throws NoSuchArgumentException
159
     */
160
    protected function initializeAction()
161
    {
162
        parent::initializeAction();
163
        $this->moduleTemplate = $this->moduleTemplateFactory->create($this->request);
164
        if ($this->request->hasArgument('id')) {
165
            $this->selectedPageUID = (int)$this->request->getArgument('id');
166
        }
167
168
        $this->requestedPageUID = $this->selectedPageUID;
169
170
        if ($this->autoSelectFirstSiteAndRootPageWhenOnlyOneSiteIsAvailable()) {
171
            return;
172
        }
173
174
        if ($this->selectedPageUID < 1) {
175
            return;
176
        }
177
178
        try {
179
            $this->selectedSite = $this->siteRepository->getSiteByPageId($this->selectedPageUID);
180
        } catch (InvalidArgumentException $exception) {
181
            return;
182
        }
183
    }
184
185
    /**
186
     * @return bool
187
     * @throws DBALDriverException
188
     * @throws Throwable
189
     */
190
    protected function autoSelectFirstSiteAndRootPageWhenOnlyOneSiteIsAvailable(): bool
191
    {
192
        $solrConfiguredSites = $this->siteRepository->getAvailableSites();
193
        $availableSites = $this->siteFinder->getAllSites();
194
        if (count($solrConfiguredSites) === 1 && count($availableSites) === 1) {
195
            $this->selectedSite = $this->siteRepository->getFirstAvailableSite();
196
197
            // we only overwrite the selected pageUid when no id was passed
198
            if ($this->selectedPageUID === 0) {
199
                $this->selectedPageUID = $this->selectedSite->getRootPageId();
0 ignored issues
show
Bug introduced by
The method getRootPageId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
                /** @scrutinizer ignore-call */ 
200
                $this->selectedPageUID = $this->selectedSite->getRootPageId();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
200
            }
201
            return true;
202
        }
203
204
        return false;
205
    }
206
207
    /**
208
     * Set up the doc header properly here
209
     *
210
     * @param ViewInterface $view
211
     * @throws DBALDriverException
212
     * @throws Throwable
213
     */
214
    protected function initializeView($view)
215
    {
216
        $sites = $this->siteRepository->getAvailableSites();
217
218
        $selectOtherPage = count($sites) > 0 || $this->selectedPageUID < 1;
219
        $this->view->assign('showSelectOtherPage', $selectOtherPage);
220
        $this->view->assign('pageUID', $this->selectedPageUID);
221
        if ($this->selectedPageUID < 1) {
222
            return;
223
        }
224
225
        if ($this->selectedSite === null) {
226
            return;
227
        }
228
229
        /* @var BackendUserAuthentication $beUser */
230
        $beUser = $GLOBALS['BE_USER'];
231
        $permissionClause = $beUser->getPagePermsClause(1);
232
        $pageRecord = BackendUtility::readPageAccess($this->selectedSite->getRootPageId(), $permissionClause);
233
234
        if ($pageRecord === false) {
235
            throw new InvalidArgumentException(vsprintf('There is something wrong with permissions for page "%s" for backend user "%s".', [$this->selectedSite->getRootPageId(), $beUser->user['username']]), 1496146317);
236
        }
237
        $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageRecord);
238
    }
239
240
    /**
241
     * Generates selector menu in backends doc header using selected page from page tree.
242
     *
243
     * @param string|null $uriToRedirectTo
244
     */
245
    public function generateCoreSelectorMenuUsingPageTree(string $uriToRedirectTo = null)
246
    {
247
        if ($this->selectedPageUID < 1 || $this->selectedSite === null) {
248
            return;
249
        }
250
251
        $this->generateCoreSelectorMenu($this->selectedSite, $uriToRedirectTo);
252
    }
253
254
    /**
255
     * Generates Core selector Menu for given Site.
256
     *
257
     * @param Site $site
258
     * @param string|null $uriToRedirectTo
259
     */
260
    protected function generateCoreSelectorMenu(Site $site, string $uriToRedirectTo = null)
261
    {
262
        $this->coreSelectorMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
263
        $this->coreSelectorMenu->setIdentifier('component_core_selector_menu');
264
265
        if (!isset($uriToRedirectTo)) {
266
            $uriToRedirectTo = $this->uriBuilder->reset()->uriFor();
267
        }
268
269
        $this->initializeSelectedSolrCoreConnection();
270
        $cores = $this->solrConnectionManager->getConnectionsBySite($site);
271
        foreach ($cores as $core) {
272
            $coreAdmin = $core->getAdminService();
273
            $menuItem = $this->coreSelectorMenu->makeMenuItem();
274
            $menuItem->setTitle($coreAdmin->getCorePath());
275
            $uri = $this->uriBuilder->reset()->uriFor(
276
                'switchCore',
277
                [
278
                    'corePath' => $coreAdmin->getCorePath(),
279
                    'uriToRedirectTo' => $uriToRedirectTo,
280
                ]
281
            );
282
            $menuItem->setHref($uri);
283
284
            if ($coreAdmin->getCorePath() == $this->selectedSolrCoreConnection->getAdminService()->getCorePath()) {
0 ignored issues
show
Bug introduced by
The method getAdminService() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

284
            if ($coreAdmin->getCorePath() == $this->selectedSolrCoreConnection->/** @scrutinizer ignore-call */ getAdminService()->getCorePath()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
285
                $menuItem->setActive(true);
286
            }
287
            $this->coreSelectorMenu->addMenuItem($menuItem);
288
        }
289
290
        $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($this->coreSelectorMenu);
291
    }
292
293
    /**
294
     * Empties the Index Queue
295
     *
296
     * @return ResponseInterface
297
     *
298
     * @noinspection PhpUnused Used in IndexQueue- and IndexAdministration- controllers
299
     */
300
    public function clearIndexQueueAction(): ResponseInterface
301
    {
302
        $this->indexQueue->deleteItemsBySite($this->selectedSite);
0 ignored issues
show
Bug introduced by
It seems like $this->selectedSite can also be of type null; however, parameter $site of ApacheSolrForTypo3\Solr\...ue::deleteItemsBySite() does only seem to accept ApacheSolrForTypo3\Solr\Domain\Site\Site, 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

302
        $this->indexQueue->deleteItemsBySite(/** @scrutinizer ignore-type */ $this->selectedSite);
Loading history...
303
        $this->addFlashMessage(
304
            LocalizationUtility::translate(
305
                'solr.backend.index_administration.success.queue_emptied',
306
                'Solr',
307
                [$this->selectedSite->getLabel()]
308
            )
309
        );
310
311
        return new RedirectResponse($this->uriBuilder->uriFor('index'), 303);
312
    }
313
314
    /**
315
     * Switches used core.
316
     *
317
     * Note: Does not check availability of core in site. All this stuff is done in the generation step.
318
     *
319
     * @param string $corePath
320
     * @param string $uriToRedirectTo
321
     *
322
     * @return ResponseInterface
323
     *
324
     * @noinspection PhpUnused Used in IndexQueue- and IndexAdministration- controllers
325
     */
326
    public function switchCoreAction(string $corePath, string $uriToRedirectTo): ResponseInterface
327
    {
328
        $moduleData = $this->moduleDataStorageService->loadModuleData();
329
        $moduleData->setCore($corePath);
330
331
        $this->moduleDataStorageService->persistModuleData($moduleData);
332
        $message = LocalizationUtility::translate('coreselector_switched_successfully', 'solr', [$corePath]);
333
        $this->addFlashMessage($message);
334
        return new RedirectResponse($uriToRedirectTo, 303);
335
    }
336
337
    /**
338
     * Returns the Response for be module action.
339
     *
340
     * @return ResponseInterface
341
     */
342
    protected function getModuleTemplateResponse(): ResponseInterface
343
    {
344
        $this->moduleTemplate->setContent($this->view->render());
345
        return $this->htmlResponse($this->moduleTemplate->renderContent());
346
    }
347
348
    /**
349
     * Initializes the solr core connection considerately to the components state.
350
     * Uses and persists default core connection if persisted core in Site does not exist.
351
     */
352
    private function initializeSelectedSolrCoreConnection()
353
    {
354
        $moduleData = $this->moduleDataStorageService->loadModuleData();
355
356
        $solrCoreConnections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
0 ignored issues
show
Bug introduced by
It seems like $this->selectedSite can also be of type null; however, parameter $site of ApacheSolrForTypo3\Solr\...:getConnectionsBySite() does only seem to accept ApacheSolrForTypo3\Solr\Domain\Site\Site, 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

356
        $solrCoreConnections = $this->solrConnectionManager->getConnectionsBySite(/** @scrutinizer ignore-type */ $this->selectedSite);
Loading history...
357
        $currentSolrCorePath = $moduleData->getCore();
358
        if (empty($currentSolrCorePath)) {
359
            $this->initializeFirstAvailableSolrCoreConnection($solrCoreConnections, $moduleData);
360
            return;
361
        }
362
        foreach ($solrCoreConnections as $solrCoreConnection) {
363
            if ($solrCoreConnection->getAdminService()->getCorePath() == $currentSolrCorePath) {
364
                $this->selectedSolrCoreConnection = $solrCoreConnection;
365
            }
366
        }
367
        if (!$this->selectedSolrCoreConnection instanceof SolrCoreConnection && count($solrCoreConnections) > 0) {
368
            $this->initializeFirstAvailableSolrCoreConnection($solrCoreConnections, $moduleData);
369
            $message = LocalizationUtility::translate('coreselector_switched_to_default_core', 'solr', [$currentSolrCorePath, $this->selectedSite->getLabel(), $this->selectedSolrCoreConnection->getAdminService()->getCorePath()]);
370
            $this->addFlashMessage($message, '', AbstractMessage::NOTICE);
371
        }
372
    }
373
374
    /**
375
     * @param SolrCoreConnection[] $solrCoreConnections
376
     */
377
    private function initializeFirstAvailableSolrCoreConnection(array $solrCoreConnections, $moduleData)
378
    {
379
        if (empty($solrCoreConnections)) {
380
            return;
381
        }
382
        $this->selectedSolrCoreConnection = array_shift($solrCoreConnections);
383
        $moduleData->setCore($this->selectedSolrCoreConnection->getAdminService()->getCorePath());
384
        $this->moduleDataStorageService->persistModuleData($moduleData);
385
    }
386
}
387