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