[TASK] Use ModuleTemplate API for EXT:indexed_search
[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
17 use TYPO3\CMS\Backend\View\BackendTemplateView;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Database\DatabaseConnection;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
22 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
23 use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
24 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
25 use TYPO3\CMS\IndexedSearch\Domain\Repository\AdministrationRepository;
26 use TYPO3\CMS\Backend\Utility\BackendUtility;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\IndexedSearch\Indexer;
29 use TYPO3\CMS\Lang\LanguageService;
30
31 /**
32 * Administration controller
33 */
34 class AdministrationController extends ActionController {
35
36 /**
37 * @var AdministrationRepository
38 */
39 protected $administrationRepository;
40
41 /**
42 * @var int Current page id
43 */
44 protected $pageUid = 0;
45
46 /**
47 * @var array External parsers
48 */
49 protected $external_parsers = array();
50
51 /**
52 * @var array Configuration defined in the Extension Manager
53 */
54 protected $indexerConfig = array();
55
56 /**
57 * @var bool is metaphone enabled
58 */
59 protected $enableMetaphoneSearch = FALSE;
60
61 /**
62 * Indexer object
63 *
64 * @var \TYPO3\CMS\IndexedSearch\Indexer
65 */
66 protected $indexer;
67
68 /**
69 * Backend Template Container
70 *
71 * @var BackendTemplateView
72 */
73 protected $defaultViewObjectName = BackendTemplateView::class;
74
75 /**
76 * BackendTemplateContainer
77 *
78 * @var BackendTemplateView
79 */
80 protected $view;
81
82 /**
83 * Set up the doc header properly here
84 *
85 * @param ViewInterface $view
86 */
87 protected function initializeView(ViewInterface $view) {
88 /** @var BackendTemplateView $view */
89 parent::initializeView($view);
90 $permissionClause = $this->getBackendUserAuthentication()->getPagePermsClause(1);
91 $pageRecord = BackendUtility::readPageAccess($this->pageUid, $permissionClause);
92 $view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation($pageRecord);
93 $this->generateMenu();
94 }
95
96 /**
97 * Generates the action menu
98 */
99 protected function generateMenu() {
100 $menuItems = [
101 'index' => [
102 'controller' => 'Administration',
103 'action' => 'index',
104 'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xml:administration.menu.general')
105 ],
106 'pages' => [
107 'controller' => 'Administration',
108 'action' => 'pages',
109 'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xml:administration.menu.pages')
110 ],
111 'externalDocuments' => [
112 'controller' => 'Administration',
113 'action' => 'externalDocuments',
114 'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xml:administration.menu.externalDocuments')
115 ],
116 'statistic' => [
117 'controller' => 'Administration',
118 'action' => 'statistic',
119 'label' => $this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xml:administration.menu.statistic')
120 ]
121 ];
122 $uriBuilder = $this->objectManager->get(UriBuilder::class);
123 $uriBuilder->setRequest($this->request);
124
125 $menu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
126 $menu->setIdentifier('IndexedSearchModuleMenu');
127
128 foreach ($menuItems as $menuItemConfig) {
129 $isActive = $this->request->getControllerActionName() === $menuItemConfig['action'];
130 $menuItem = $menu->makeMenuItem()
131 ->setTitle($menuItemConfig['label'])
132 ->setHref($this->getHref($menuItemConfig['controller'], $menuItemConfig['action']))
133 ->setActive($isActive);
134 $menu->addMenuItem($menuItem);
135 }
136
137 $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
138 }
139
140 /**
141 * Function will be called before every other action
142 *
143 * @return void
144 */
145 public function initializeAction() {
146 $this->pageUid = (int)GeneralUtility::_GET('id');
147 $this->indexerConfig = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['indexed_search']);
148 $this->enableMetaphoneSearch = (bool)$this->indexerConfig['enableMetaphoneSearch'];
149 $this->indexer = GeneralUtility::makeInstance(Indexer::class);
150
151 parent::initializeAction();
152 }
153
154 /**
155 * Override the action name if found in the uc of the user
156 *
157 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
158 * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response
159 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
160 */
161 public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) {
162 $vars = GeneralUtility::_GET('tx_indexedsearch_web_indexedsearchisearch');
163
164 $beUser = $this->getBackendUserAuthentication();
165 if (is_array($vars) && isset($vars['action']) && method_exists($this, $vars['action'] . 'Action')) {
166 $action = $vars['action'];
167
168 switch($action) {
169 case 'saveStopwordsKeywords':
170 $action = 'statisticDetails';
171 break;
172 case 'deleteIndexedItem':
173 $action = 'statistic';
174 break;
175 }
176
177 $beUser->uc['indexed_search']['action'] = $action;
178 $beUser->uc['indexed_search']['arguments'] = $request->getArguments();
179 $beUser->writeUC();
180 } elseif (isset($beUser->uc['indexed_search']['action'])) {
181 if ($request instanceof WebRequest) {
182 $request->setControllerActionName($beUser->uc['indexed_search']['action']);
183 }
184 if (isset($beUser->uc['indexed_search']['arguments'])) {
185 $request->setArguments($beUser->uc['indexed_search']['arguments']);
186 }
187 }
188
189 parent::processRequest($request, $response);
190 }
191
192 /**
193 * @param \TYPO3\CMS\IndexedSearch\Domain\Repository\AdministrationRepository $administrationRepository
194 * @return void
195 */
196 public function injectAdministrationRepository(AdministrationRepository $administrationRepository) {
197 $this->administrationRepository = $administrationRepository;
198 }
199
200 /**
201 * Index action contains the most important statistics
202 *
203 * @return void
204 */
205 public function indexAction() {
206 $this->view->assignMultiple(array(
207 'records' => $this->administrationRepository->getRecordsNumbers(),
208 'phash' => $this->administrationRepository->getPageHashTypes()
209 ));
210
211 if ($this->pageUid) {
212 $last24hours = ' AND tstamp > ' . ($GLOBALS['EXEC_TIME'] - 24 * 60 * 60);
213 $last30days = ' AND tstamp > ' . ($GLOBALS['EXEC_TIME'] - 30 * 24 * 60 * 60);
214
215 $this->view->assignMultiple(array(
216 'pageUid' => $this->pageUid,
217 'all' => $this->administrationRepository->getGeneralSearchStatistic('', $this->pageUid),
218 'last24hours' => $this->administrationRepository->getGeneralSearchStatistic($last24hours, $this->pageUid),
219 'last30days' => $this->administrationRepository->getGeneralSearchStatistic($last30days, $this->pageUid),
220 ));
221 }
222 }
223
224 /**
225 * Statistics for pages
226 *
227 * @return void
228 */
229 public function pagesAction() {
230 $this->view->assign('records', $this->administrationRepository->getPageStatistic());
231 }
232
233 /**
234 * Statistics for external documents
235 *
236 * @return void
237 */
238 public function externalDocumentsAction() {
239 $this->view->assign('records', $this->administrationRepository->getExternalDocumentsStatistic());
240 }
241
242 /**
243 * Statistics for a given page hash
244 *
245 * @param int $pageHash
246 * @return void
247 */
248 public function statisticDetailsAction($pageHash = 0) {
249 // Set back button
250 $icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-up', Icon::SIZE_SMALL);
251 $backButton = $this->view->getModuleTemplate()->getDocHeaderComponent()
252 ->getButtonBar()->makeLinkButton()
253 ->setTitle($this->getLanguageService()->sL('LLL:EXT:indexed_search/Resources/Private/Language/locallang.xml:administration.back'))
254 ->setIcon($icon)
255 ->setHref($this->getHref('Administration', 'statistic'));
256 $this->view->getModuleTemplate()->getDocHeaderComponent()
257 ->getButtonBar()->addButton($backButton);
258
259 $pageHash = (int)$pageHash;
260 $db = $this->getDatabaseConnection();
261 $pageHashRow = $db->exec_SELECTgetSingleRow('*', 'index_phash', 'phash = ' . (int)$pageHash);
262
263 if (!is_array($pageHashRow)) {
264 $this->redirect('statistic');
265 }
266
267 $debugRow = $db->exec_SELECTgetRows('*', 'index_debug', 'phash = ' . (int)$pageHash);
268 $debugInfo = array();
269 $lexer = '';
270 if (is_array($debugRow)) {
271 $debugInfo = unserialize($debugRow[0]['debuginfo']);
272 $lexer = $debugInfo['lexer'];
273 unset($debugInfo['lexer']);
274 }
275 $pageRecord = BackendUtility::getRecord('pages', $pageHashRow['data_page_id']);
276 $keywords = is_array($pageRecord) ? array_flip(GeneralUtility::trimExplode(',', $pageRecord['keywords'], TRUE)) : array();
277 $wordRecords = $db->exec_SELECTgetRows(
278 'index_words.*, index_rel.*',
279 'index_rel, index_words',
280 'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid',
281 '',
282 'index_words.baseword'
283 );
284 foreach($wordRecords as $id => $row) {
285 if (isset($keywords[$row['baseword']])) {
286 $wordRecords[$id]['is_keyword'] = TRUE;
287 }
288 }
289 $metaphoneRows = $metaphone = array();
290 if ($this->enableMetaphoneSearch && is_array($wordRecords)) {
291 // Group metaphone hash
292 foreach ($wordRecords as $row) {
293 $metaphoneRows[$row['metaphone']][] = $row['baseword'];
294 }
295
296 foreach ($metaphoneRows as $hash => $words) {
297 if (count($words) > 1) {
298 $metaphone[] = array(
299 'metaphone' => $this->indexer->metaphone($words[0], 1), $hash,
300 'words' => $words,
301 'hash' => $hash
302 );
303 }
304 }
305 }
306 $this->view->assignMultiple(array(
307 'phash' => $pageHash,
308 'phashRow' => $pageHashRow,
309 'words' => $wordRecords,
310 'sections' => $db->exec_SELECTgetRows(
311 '*',
312 'index_section',
313 'index_section.phash = ' . (int)$pageHash
314 ),
315 'topCount' => $db->exec_SELECTgetRows(
316 'index_words.baseword, index_words.metaphone, index_rel.*',
317 'index_rel, index_words',
318 'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid
319 AND index_words.is_stopword=0',
320 '',
321 'index_rel.count DESC',
322 '20'
323 ),
324 'topFrequency' => $db->exec_SELECTgetRows(
325 'index_words.baseword, index_words.metaphone, index_rel.*',
326 'index_rel, index_words',
327 'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid
328 AND index_words.is_stopword=0',
329 '',
330 'index_rel.freq DESC',
331 '20'
332 ),
333 'debug' => $debugInfo,
334 'lexer' => $lexer,
335 'metaphone' => $metaphone,
336 'page' => $pageRecord,
337 'keywords' => $keywords
338 ));
339 }
340
341 /**
342 * Save stop words and keywords
343 *
344 * @param string $pageHash
345 * @param int $pageId
346 * @param array $stopwords
347 * @param array $keywords
348 * @return void
349 */
350 public function saveStopwordsKeywordsAction($pageHash, $pageId, $stopwords = array(), $keywords = array()) {
351 if ($this->getBackendUserAuthentication()->isAdmin()) {
352 if (is_array($stopwords) && !empty($stopwords)) {
353 $this->administrationRepository->saveStopWords($stopwords);
354 }
355 if (is_array($keywords) && !empty($keywords)) {
356 $this->administrationRepository->saveKeywords($keywords, $pageId);
357 }
358 }
359
360 $this->redirect('statisticDetails', NULL, NULL, array('pageHash' => $pageHash));
361 }
362
363 /**
364 * Statistics for a given word id
365 *
366 * @param int $id
367 * @param int $pageHash
368 * @return void
369 */
370 public function wordDetailAction($id = 0, $pageHash = 0) {
371 $rows = $this->getDatabaseConnection()->exec_SELECTgetRows(
372 'index_phash.*, index_section.*, index_rel.*',
373 'index_rel, index_section, index_phash',
374 'index_rel.wid = ' . (int)$id . ' AND index_rel.phash = index_section.phash' . ' AND index_section.phash = index_phash.phash',
375 '',
376 'index_rel.freq DESC'
377 );
378
379 $this->view->assignMultiple(array(
380 'rows' => $rows,
381 'phash' => $pageHash
382 ));
383 }
384
385 /**
386 * General statistics
387 *
388 * @param int $depth
389 * @param string $mode
390 * @return void
391 */
392 public function statisticAction($depth = 1, $mode = 'overview') {
393 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'])) {
394 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] as $extension => $_objRef) {
395 /** @var \TYPO3\CMS\IndexedSearch\FileContentParser $fileContentParser */
396 $fileContentParser = GeneralUtility::getUserObj($_objRef);
397 if ($fileContentParser->softInit($extension)) {
398 $this->external_parsers[$extension] = $fileContentParser;
399 }
400 }
401 }
402 $this->administrationRepository->external_parsers = $this->external_parsers;
403
404 $allLines = $this->administrationRepository->getTree($this->pageUid, $depth, $mode);
405
406 $this->view->assignMultiple(array(
407 'levelTranslations' => explode('|', $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.enterSearchLevels')),
408 'tree' => $allLines,
409 'pageUid' => $this->pageUid,
410 'mode' => $mode,
411 'depth' => $depth
412 ));
413 }
414
415
416 /**
417 * Remove item from index
418 *
419 * @param string $id
420 * @param int $depth
421 * @param string $mode
422 * @return void
423 */
424 public function deleteIndexedItemAction($id, $depth = 1, $mode = 'overview') {
425 $this->administrationRepository->removeIndexedPhashRow($id, $this->pageUid, $depth);
426 $this->redirect('statistic', NULL, NULL, array('depth' => $depth, 'mode' => $mode));
427 }
428
429 /**
430 * Creates te URI for a backend action
431 *
432 * @param string $controller
433 * @param string $action
434 * @param array $parameters
435 *
436 * @return string
437 */
438 protected function getHref($controller, $action, $parameters = []) {
439 $uriBuilder = $this->objectManager->get(UriBuilder::class);
440 $uriBuilder->setRequest($this->request);
441 return $uriBuilder->reset()->uriFor($action, $parameters, $controller);
442 }
443
444 /**
445 * @return DatabaseConnection
446 */
447 protected function getDatabaseConnection() {
448 return $GLOBALS['TYPO3_DB'];
449 }
450
451 /**
452 * @return BackendUserAuthentication
453 */
454 protected function getBackendUserAuthentication() {
455 return $GLOBALS['BE_USER'];
456 }
457
458 /**
459 * @return LanguageService
460 */
461 protected function getLanguageService() {
462 return $GLOBALS['LANG'];
463 }
464
465 }