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