Passed
Push — master ( 3f5328...bede2c )
by
unknown
17:46
created

AdministrationController   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 532
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 45
eloc 265
c 2
b 0
f 0
dl 0
loc 532
rs 8.8

16 Methods

Rating   Name   Duplication   Size   Complexity  
A injectAdministrationRepository() 0 3 1
A initializeAction() 0 8 1
A initializeView() 0 13 3
A statisticAction() 0 22 3
A deleteIndexedItemAction() 0 4 1
A externalDocumentsAction() 0 5 1
A indexAction() 0 24 2
B processRequest() 0 30 9
C statisticDetailsAction() 0 172 11
A generateMenu() 0 38 2
A getServerRequest() 0 3 1
A getBackendUserAuthentication() 0 3 1
A saveStopwordsKeywordsAction() 0 12 6
A getLanguageService() 0 3 1
A pagesAction() 0 5 1
A wordDetailAction() 0 32 1

How to fix   Complexity   

Complex Class

Complex classes like AdministrationController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdministrationController, and based on these observations, apply Extract Interface, too.

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 TYPO3\CMS\IndexedSearch\Controller;
17
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Backend\View\BackendTemplateView;
22
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
24
use TYPO3\CMS\Core\Database\ConnectionPool;
25
use TYPO3\CMS\Core\Imaging\Icon;
26
use TYPO3\CMS\Core\Localization\LanguageService;
27
use TYPO3\CMS\Core\Type\Bitmask\Permission;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
30
use TYPO3\CMS\Extbase\Mvc\Request;
31
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
32
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
33
use TYPO3\CMS\IndexedSearch\Domain\Repository\AdministrationRepository;
34
use TYPO3\CMS\IndexedSearch\Indexer;
35
36
/**
37
 * Administration controller
38
 * @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
39
 */
40
class AdministrationController extends ActionController
41
{
42
    /**
43
     * @var AdministrationRepository
44
     */
45
    protected $administrationRepository;
46
47
    /**
48
     * @var int Current page id
49
     */
50
    protected $pageUid = 0;
51
52
    /**
53
     * @var array External parsers
54
     */
55
    protected $external_parsers = [];
56
57
    /**
58
     * @var array Configuration defined in the Extension Manager
59
     */
60
    protected $indexerConfig = [];
61
62
    /**
63
     * @var bool is metaphone enabled
64
     */
65
    protected $enableMetaphoneSearch = false;
66
67
    /**
68
     * Indexer object
69
     *
70
     * @var \TYPO3\CMS\IndexedSearch\Indexer
71
     */
72
    protected $indexer;
73
74
    /**
75
     * Backend Template Container
76
     *
77
     * @var string
78
     */
79
    protected $defaultViewObjectName = BackendTemplateView::class;
80
81
    /**
82
     * BackendTemplateContainer
83
     *
84
     * @var BackendTemplateView
85
     */
86
    protected $view;
87
88
    /**
89
     * Set up the doc header properly here
90
     *
91
     * @param ViewInterface $view
92
     */
93
    protected function initializeView(ViewInterface $view)
94
    {
95
        if ($view instanceof BackendTemplateView) {
96
            /** @var BackendTemplateView $view */
97
            parent::initializeView($view);
98
            $permissionClause = $this->getBackendUserAuthentication()->getPagePermsClause(Permission::PAGE_SHOW);
99
            $pageRecord = BackendUtility::readPageAccess($this->pageUid, $permissionClause);
100
            if ($pageRecord) {
101
                $view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation($pageRecord);
102
            }
103
            $this->generateMenu();
104
            $this->view->getModuleTemplate()->setFlashMessageQueue($this->getFlashMessageQueue());
105
            $view->assign('extensionConfiguration', $this->indexerConfig);
106
        }
107
    }
108
109
    /**
110
     * Generates the action menu
111
     */
112
    protected function generateMenu()
113
    {
114
        $menuItems = [
115
            'index' => [
116
                'controller' => 'Administration',
117
                'action' => 'index',
118
                'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:administration.menu.general')
119
            ],
120
            'pages' => [
121
                'controller' => 'Administration',
122
                'action' => 'pages',
123
                'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:administration.menu.pages')
124
            ],
125
            'externalDocuments' => [
126
                'controller' => 'Administration',
127
                'action' => 'externalDocuments',
128
                'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:administration.menu.externalDocuments')
129
            ],
130
            'statistic' => [
131
                'controller' => 'Administration',
132
                'action' => 'statistic',
133
                'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:administration.menu.statistic')
134
            ]
135
        ];
136
137
        $menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
138
        $menu->setIdentifier('IndexedSearchModuleMenu');
139
140
        foreach ($menuItems as $menuItemConfig) {
141
            $isActive = $this->request->getControllerActionName() === $menuItemConfig['action'];
142
            $menuItem = $menu->makeMenuItem()
143
                ->setTitle($menuItemConfig['label'])
144
                ->setHref($this->uriBuilder->reset()->uriFor($menuItemConfig['action'], [], $menuItemConfig['controller']))
145
                ->setActive($isActive);
146
            $menu->addMenuItem($menuItem);
147
        }
148
149
        $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
150
    }
151
152
    /**
153
     * Function will be called before every other action
154
     */
155
    public function initializeAction()
156
    {
157
        $this->pageUid = (int)($this->getServerRequest()->getQueryParams()['id'] ?? 0);
158
        $this->indexerConfig = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search');
159
        $this->enableMetaphoneSearch = (bool)$this->indexerConfig['enableMetaphoneSearch'];
160
        $this->indexer = GeneralUtility::makeInstance(Indexer::class);
161
162
        parent::initializeAction();
163
    }
164
165
    /**
166
     * Override the action name if found in the uc of the user
167
     *
168
     * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
169
     * @return ResponseInterface
170
     */
171
    public function processRequest(RequestInterface $request): ResponseInterface
172
    {
173
        $arguments = $request->getArguments();
174
        $beUser = $this->getBackendUserAuthentication();
175
176
        if (is_array($arguments) && isset($arguments['action']) && method_exists($this, $arguments['action'] . 'Action')) {
177
            $action = $arguments['action'];
178
179
            switch ($action) {
180
                case 'saveStopwordsKeywords':
181
                    $action = 'statisticDetails';
182
                    break;
183
                case 'deleteIndexedItem':
184
                    $action = 'statistic';
185
                    break;
186
            }
187
188
            $beUser->uc['indexed_search']['action'] = $action;
189
            $beUser->uc['indexed_search']['arguments'] = $arguments;
190
            $beUser->writeUC();
191
        } elseif (isset($beUser->uc['indexed_search']['action'])) {
192
            if ($request instanceof Request) {
193
                $request->setControllerActionName($beUser->uc['indexed_search']['action']);
194
            }
195
            if (isset($beUser->uc['indexed_search']['arguments'])) {
196
                $request->setArguments($beUser->uc['indexed_search']['arguments']);
197
            }
198
        }
199
200
        return parent::processRequest($request);
201
    }
202
203
    /**
204
     * @param \TYPO3\CMS\IndexedSearch\Domain\Repository\AdministrationRepository $administrationRepository
205
     */
206
    public function injectAdministrationRepository(AdministrationRepository $administrationRepository)
207
    {
208
        $this->administrationRepository = $administrationRepository;
209
    }
210
211
    /**
212
     * Index action contains the most important statistics
213
     */
214
    public function indexAction(): ResponseInterface
215
    {
216
        $this->view->assignMultiple([
217
            'records' => $this->administrationRepository->getRecordsNumbers(),
218
            'phash' => $this->administrationRepository->getPageHashTypes()
219
        ]);
220
221
        if ($this->pageUid) {
222
            $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
223
                ->getQueryBuilderForTable('index_stat_word')
224
                ->expr();
225
226
            $last24hours = $expressionBuilder->gt('tstamp', $GLOBALS['EXEC_TIME'] - 86400);
227
            $last30days = $expressionBuilder->gt('tstamp', $GLOBALS['EXEC_TIME'] - 30 * 86400);
228
229
            $this->view->assignMultiple([
230
                'pageUid' => $this->pageUid,
231
                'all' => $this->administrationRepository->getGeneralSearchStatistic('', $this->pageUid),
232
                'last24hours' => $this->administrationRepository->getGeneralSearchStatistic($last24hours, $this->pageUid),
233
                'last30days' => $this->administrationRepository->getGeneralSearchStatistic($last30days, $this->pageUid),
234
            ]);
235
        }
236
237
        return $this->htmlResponse($this->view->render());
238
    }
239
240
    /**
241
     * Statistics for pages
242
     */
243
    public function pagesAction(): ResponseInterface
244
    {
245
        $this->view->assign('records', $this->administrationRepository->getPageStatistic());
246
247
        return $this->htmlResponse($this->view->render());
248
    }
249
250
    /**
251
     * Statistics for external documents
252
     */
253
    public function externalDocumentsAction(): ResponseInterface
254
    {
255
        $this->view->assign('records', $this->administrationRepository->getExternalDocumentsStatistic());
256
257
        return $this->htmlResponse($this->view->render());
258
    }
259
260
    /**
261
     * Statistics for a given page hash
262
     *
263
     * @param int $pageHash
264
     */
265
    public function statisticDetailsAction($pageHash = 0): ResponseInterface
266
    {
267
        $pageHash = (int)$pageHash;
268
        // Set back button
269
        $icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-up', Icon::SIZE_SMALL);
270
        $backButton = $this->view->getModuleTemplate()->getDocHeaderComponent()
271
            ->getButtonBar()->makeLinkButton()
272
            ->setTitle($this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:administration.back'))
273
            ->setIcon($icon)
274
            ->setHref($this->uriBuilder->reset()->uriFor('statistic', [], 'Administration'));
275
        $this->view->getModuleTemplate()->getDocHeaderComponent()
276
            ->getButtonBar()->addButton($backButton);
277
278
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
279
        $pageHashRow = $queryBuilder
280
            ->select('*')
281
            ->from('index_phash')
282
            ->where(
283
                $queryBuilder->expr()->eq(
284
                    'phash',
285
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
286
                )
287
            )
288
            ->execute()
289
            ->fetch();
290
291
        if (!is_array($pageHashRow)) {
292
            $this->redirect('statistic');
293
        }
294
295
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_debug');
296
        $debugRow = $queryBuilder
297
            ->select('debuginfo')
298
            ->from('index_debug')
299
            ->where(
300
                $queryBuilder->expr()->eq(
301
                    'phash',
302
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
303
                )
304
            )
305
            ->execute()
306
            ->fetch();
307
        $debugInfo = [];
308
        $lexer = '';
309
        if (is_array($debugRow)) {
310
            $debugInfo = json_decode($debugRow['debuginfo'], true);
311
            $lexer = $debugInfo['lexer'];
312
            unset($debugInfo['lexer']);
313
        }
314
        $pageRecord = BackendUtility::getRecord('pages', $pageHashRow['data_page_id']);
315
        $keywords = is_array($pageRecord) ? array_flip(GeneralUtility::trimExplode(',', $pageRecord['keywords'], true)) : [];
316
317
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
318
        $wordRecords = $queryBuilder
319
            ->select('index_words.*', 'index_rel.*')
320
            ->from('index_words')
321
            ->from('index_rel')
322
            ->where(
323
                $queryBuilder->expr()->eq(
324
                    'index_rel.phash',
325
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
326
                ),
327
                $queryBuilder->expr()->eq(
328
                    'index_words.wid',
329
                    $queryBuilder->quoteIdentifier('index_rel.wid')
330
                )
331
            )
332
            ->orderBy('index_words.baseword')
333
            ->execute()
334
            ->fetchAll();
335
        foreach ($wordRecords as $id => $row) {
336
            if (isset($keywords[$row['baseword']])) {
337
                $wordRecords[$id]['is_keyword'] = true;
338
            }
339
        }
340
        $metaphoneRows = $metaphone = [];
341
        if ($this->enableMetaphoneSearch && is_array($wordRecords)) {
342
            // Group metaphone hash
343
            foreach ($wordRecords as $row) {
344
                $metaphoneRows[$row['metaphone']][] = $row['baseword'];
345
            }
346
347
            foreach ($metaphoneRows as $hash => $words) {
348
                if (count($words) > 1) {
349
                    $metaphone[] = [
350
                        'metaphone' => $this->indexer->metaphone($words[0], 1), $hash,
351
                        'words' => $words,
352
                        'hash' => $hash
353
                    ];
354
                }
355
            }
356
        }
357
358
        // sections
359
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
360
        $sections = $queryBuilder
361
            ->select('*')
362
            ->from('index_section')
363
            ->where(
364
                $queryBuilder->expr()->eq(
365
                    'phash',
366
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
367
                )
368
            )
369
            ->execute()
370
            ->fetchAll();
371
372
        // top words
373
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
374
        $topCountWords = $queryBuilder
375
            ->select('index_words.baseword', 'index_words.metaphone', 'index_rel.*')
376
            ->from('index_words')
377
            ->from('index_rel')
378
            ->setMaxResults(20)
379
            ->where(
380
                $queryBuilder->expr()->eq(
381
                    'index_rel.phash',
382
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
383
                ),
384
                $queryBuilder->expr()->eq(
385
                    'index_words.is_stopword',
386
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
387
                ),
388
                $queryBuilder->expr()->eq(
389
                    'index_words.wid',
390
                    $queryBuilder->quoteIdentifier('index_rel.wid')
391
                )
392
            )
393
            ->orderBy('index_rel.count', 'DESC')
394
            ->execute()
395
            ->fetchAll();
396
397
        // top frequency
398
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
399
        $topFrequency = $queryBuilder
400
            ->select('index_words.baseword', 'index_words.metaphone', 'index_rel.*')
401
            ->from('index_words')
402
            ->from('index_rel')
403
            ->setMaxResults(20)
404
            ->where(
405
                $queryBuilder->expr()->eq(
406
                    'index_rel.phash',
407
                    $queryBuilder->createNamedParameter($pageHash, \PDO::PARAM_INT)
408
                ),
409
                $queryBuilder->expr()->eq(
410
                    'index_words.is_stopword',
411
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
412
                ),
413
                $queryBuilder->expr()->eq(
414
                    'index_words.wid',
415
                    $queryBuilder->quoteIdentifier('index_rel.wid')
416
                )
417
            )
418
            ->orderBy('index_rel.freq', 'DESC')
419
            ->execute()
420
            ->fetchAll();
421
422
        $this->view->assignMultiple([
423
            'phash' => (int)$pageHash,
424
            'phashRow' => $pageHashRow,
425
            'words' => $wordRecords,
426
            'sections' => $sections,
427
            'topCount' => $topCountWords,
428
            'topFrequency' => $topFrequency,
429
            'debug' => $debugInfo,
430
            'lexer' => $lexer,
431
            'metaphone' => $metaphone,
432
            'page' => $pageRecord,
433
            'keywords' => $keywords
434
        ]);
435
436
        return $this->htmlResponse($this->view->render());
437
    }
438
439
    /**
440
     * Save stop words and keywords
441
     *
442
     * @param string $pageHash
443
     * @param int $pageId
444
     * @param array $stopwords
445
     * @param array $keywords
446
     */
447
    public function saveStopwordsKeywordsAction($pageHash, $pageId, $stopwords = [], $keywords = [])
448
    {
449
        if ($this->getBackendUserAuthentication()->isAdmin()) {
450
            if (is_array($stopwords) && !empty($stopwords)) {
451
                $this->administrationRepository->saveStopWords($stopwords);
452
            }
453
            if (is_array($keywords) && !empty($keywords)) {
454
                $this->administrationRepository->saveKeywords($keywords, $pageId);
455
            }
456
        }
457
458
        $this->redirect('statisticDetails', null, null, ['pageHash' => $pageHash]);
459
    }
460
461
    /**
462
     * Statistics for a given word id
463
     *
464
     * @param int $id
465
     * @param int $pageHash
466
     */
467
    public function wordDetailAction($id = 0, $pageHash = 0): ResponseInterface
468
    {
469
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
470
        $rows = $queryBuilder
471
            ->select('index_phash.*', 'index_section.*', 'index_rel.*')
472
            ->from('index_rel')
473
            ->from('index_section')
474
            ->from('index_phash')
475
            ->where(
476
                $queryBuilder->expr()->eq(
477
                    'index_rel.wid',
478
                    $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
479
                ),
480
                $queryBuilder->expr()->eq(
481
                    'index_rel.phash',
482
                    $queryBuilder->quoteIdentifier('index_section.phash')
483
                ),
484
                $queryBuilder->expr()->eq(
485
                    'index_section.phash',
486
                    $queryBuilder->quoteIdentifier('index_phash.phash')
487
                )
488
            )
489
            ->orderBy('index_rel.freq', 'desc')
490
            ->execute()
491
            ->fetchAll();
492
493
        $this->view->assignMultiple([
494
            'rows' => $rows,
495
            'phash' => $pageHash
496
        ]);
497
498
        return $this->htmlResponse($this->view->render());
499
    }
500
501
    /**
502
     * General statistics
503
     *
504
     * @param int $depth
505
     * @param string $mode
506
     */
507
    public function statisticAction($depth = 1, $mode = 'overview'): ResponseInterface
508
    {
509
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] ?? [] as $extension => $className) {
510
            /** @var \TYPO3\CMS\IndexedSearch\FileContentParser $fileContentParser */
511
            $fileContentParser = GeneralUtility::makeInstance($className);
512
            if ($fileContentParser->softInit($extension)) {
513
                $this->external_parsers[$extension] = $fileContentParser;
514
            }
515
        }
516
        $this->administrationRepository->external_parsers = $this->external_parsers;
517
518
        $allLines = $this->administrationRepository->getTree($this->pageUid, $depth, $mode);
519
520
        $this->view->assignMultiple([
521
            'levelTranslations' => explode('|', $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enterSearchLevels')),
522
            'tree' => $allLines,
523
            'pageUid' => $this->pageUid,
524
            'mode' => $mode,
525
            'depth' => $depth
526
        ]);
527
528
        return $this->htmlResponse($this->view->render());
529
    }
530
531
    /**
532
     * Remove item from index
533
     *
534
     * @param string $id
535
     * @param int $depth
536
     * @param string $mode
537
     */
538
    public function deleteIndexedItemAction($id, $depth = 1, $mode = 'overview')
539
    {
540
        $this->administrationRepository->removeIndexedPhashRow($id, $this->pageUid, $depth);
541
        $this->redirect('statistic', null, null, ['depth' => $depth, 'mode' => $mode]);
542
    }
543
544
    /**
545
     * @return BackendUserAuthentication
546
     */
547
    protected function getBackendUserAuthentication()
548
    {
549
        return $GLOBALS['BE_USER'];
550
    }
551
552
    /**
553
     * @return LanguageService
554
     */
555
    protected function getLanguageService()
556
    {
557
        return $GLOBALS['LANG'];
558
    }
559
560
    /**
561
     * We currently rely on the PSR-7 request next to the extbase specific
562
     * implementation, since we have to access non-prefixed query arguments.
563
     * See initializeAction(), which fetches the `id` (UID of the selected
564
     * page in the page tree).
565
     *
566
     * @return ServerRequestInterface
567
     * @todo Remove as soon as extbase uses the PSR-7 request
568
     */
569
    protected function getServerRequest(): ServerRequestInterface
570
    {
571
        return $GLOBALS['TYPO3_REQUEST'];
572
    }
573
}
574