[TASK] Turn todos into @todo to find them easier
[Packages/TYPO3.CMS.git] / typo3 / sysext / indexed_search / Classes / Controller / IndexedPagesController.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\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * Indexing class for TYPO3 frontend
22 *
23 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
24 */
25 class IndexedPagesController extends \TYPO3\CMS\Backend\Module\AbstractFunctionModule {
26
27 // Internal, dynamic:
28 public $allPhashListed = array();
29
30 // phash values accumulations for link to clear all
31 public $external_parsers = array();
32
33 // External content parsers - objects set here with file extensions as keys.
34 public $iconFileNameCache = array();
35
36 // File extensions - icon map/cache.
37 public $indexerConfig = array();
38
39 // Indexer configuration, coming from $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['indexed_search']
40 public $enableMetaphoneSearch = FALSE;
41
42 /**
43 * Indexer object
44 *
45 * @var \TYPO3\CMS\IndexedSearch\Indexer
46 */
47 public $indexerObj;
48
49 /**
50 * @int Default number of list entries per page
51 */
52 protected $maxListPerPage;
53
54 /**
55 * Default constructor loads additional locallang
56 */
57 public function __construct() {
58 $GLOBALS['LANG']->includeLLFile('EXT:indexed_search/modfunc1/locallang.xlf');
59 }
60
61 /**
62 * Initialize menu array internally
63 *
64 * @return void
65 */
66 public function modMenu() {
67 return array(
68 'depth' => array(
69 0 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_0'),
70 1 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_1'),
71 2 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_2'),
72 3 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_3'),
73 999 => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_infi')
74 ),
75 'type' => array(
76 0 => 'Overview',
77 1 => 'Technical Details',
78 2 => 'Words and content'
79 )
80 );
81 }
82
83 /**
84 * Produces main content of the module
85 *
86 * @return string HTML output
87 */
88 public function main() {
89 // Initializes the module. Done in this function because we may need to re-initialize if data is submitted!
90 // Return if no page id:
91 if ($this->pObj->id <= 0) {
92 return;
93 }
94 // Indexer configuration from Extension Manager interface:
95 $this->indexerConfig = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['indexed_search']);
96 // Workaround: If the extension configuration was not updated yet, the value is not existing
97 $this->enableMetaphoneSearch = isset($this->indexerConfig['enableMetaphoneSearch']) ? ($this->indexerConfig['enableMetaphoneSearch'] ? TRUE : FALSE) : TRUE;
98 // Initialize max-list items
99 $this->maxListPerPage = GeneralUtility::_GP('listALL') ? 100000 : 100;
100 // Processing deletion of phash rows:
101 if (GeneralUtility::_GP('deletePhash')) {
102 $this->removeIndexedPhashRow(GeneralUtility::_GP('deletePhash'));
103 }
104 // Processing stop-words:
105 if (GeneralUtility::_POST('_stopwords')) {
106 $this->processStopWords(GeneralUtility::_POST('stopWord'));
107 }
108 // Processing stop-words:
109 if (GeneralUtility::_POST('_pageKeywords')) {
110 $this->processPageKeywords(GeneralUtility::_POST('pageKeyword'), GeneralUtility::_POST('pageKeyword_pageUid'));
111 }
112 // Initialize external document parsers:
113 // Example configuration, see ext_localconf.php of this file!
114 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'])) {
115 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['external_parsers'] as $extension => $_objRef) {
116 $this->external_parsers[$extension] = GeneralUtility::getUserObj($_objRef);
117 // Init parser and if it returns FALSE, unset its entry again:
118 if (!$this->external_parsers[$extension]->softInit($extension)) {
119 unset($this->external_parsers[$extension]);
120 }
121 }
122 }
123 // Initialize indexer if we need it (metaphone display does...)
124 $this->indexerObj = GeneralUtility::makeInstance(\TYPO3\CMS\IndexedSearch\Indexer::class);
125 // Set CSS styles specific for this document:
126 $this->pObj->content = str_replace('/*###POSTCSSMARKER###*/', '
127 TABLE.c-list TR TD { white-space: nowrap; vertical-align: top; }
128 ', $this->pObj->content);
129 // Check if details for a phash record should be shown:
130 if (GeneralUtility::_GET('phash')) {
131 // Show title / function menu:
132 $theOutput .= $this->pObj->doc->spacer(5);
133 $theOutput .= $this->pObj->doc->section('Details for a single result row:', $this->showDetailsForPhash(GeneralUtility::_GET('phash')), 0, 1);
134 } elseif (GeneralUtility::_GET('wid')) {
135 // Show title / function menu:
136 $theOutput .= $this->pObj->doc->spacer(5);
137 $theOutput .= $this->pObj->doc->section('Details for a word:', $this->showDetailsForWord(GeneralUtility::_GET('wid')), 0, 1);
138 } elseif ($this->enableMetaphoneSearch && GeneralUtility::_GET('metaphone')) {
139 // Show title / function menu:
140 $theOutput .= $this->pObj->doc->spacer(5);
141 $theOutput .= $this->pObj->doc->section('Details for metaphone value:', $this->showDetailsForMetaphone(GeneralUtility::_GET('metaphone')), 0, 1);
142 } elseif (GeneralUtility::_GET('reindex')) {
143 // Show title / function menu:
144 $theOutput .= $this->pObj->doc->spacer(5);
145 $theOutput .= $this->pObj->doc->section('Reindexing...', $this->reindexPhash(GeneralUtility::_GET('reindex'), GeneralUtility::_GET('reindex_id')), 0, 1);
146 } else {
147 // Detail listings:
148 // Depth function menu:
149 $h_func = BackendUtility::getFuncMenu($this->pObj->id, 'SET[type]', $this->pObj->MOD_SETTINGS['type'], $this->pObj->MOD_MENU['type']);
150 $h_func .= BackendUtility::getFuncMenu($this->pObj->id, 'SET[depth]', $this->pObj->MOD_SETTINGS['depth'], $this->pObj->MOD_MENU['depth']);
151 // Show title / function menu:
152 $theOutput .= $this->pObj->doc->header($GLOBALS['LANG']->getLL('title'));
153 $theOutput .= $this->pObj->doc->section('', $h_func, 0, 1);
154 $theOutput .= $this->drawTableOfIndexedPages();
155 }
156 return $theOutput;
157 }
158
159 /*******************************
160 *
161 * Drawing table of indexed pages
162 *
163 ******************************/
164 /**
165 * Produces a table with indexing information for each page.
166 *
167 * @return string HTML output
168 */
169 public function drawTableOfIndexedPages() {
170 // Drawing tree:
171 $tree = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\PageTreeView::class);
172 $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
173 $tree->init('AND ' . $perms_clause);
174 $HTML = '<img src="' . $GLOBALS['BACK_PATH'] . \TYPO3\CMS\Backend\Utility\IconUtility::getIcon('pages', $this->pObj->pageinfo) . '" width="18" height="16" align="top" alt="" />';
175 $tree->tree[] = array(
176 'row' => $this->pObj->pageinfo,
177 'HTML' => $HTML
178 );
179 if ($this->pObj->MOD_SETTINGS['depth']) {
180 $tree->getTree($this->pObj->id, $this->pObj->MOD_SETTINGS['depth'], '');
181 }
182 // Traverse page tree:
183 $code = '';
184 foreach ($tree->tree as $data) {
185 $code .= $this->indexed_info($data['row'], $data['HTML'] . $this->showPageDetails(
186 BackendUtility::getRecordTitlePrep($data['row']['title']), $data['row']['uid']));
187 }
188 if ($code) {
189 $code = '<br /><br />
190 <table border="0" cellspacing="1" cellpadding="2" class="c-list">' . $this->printPhashRowHeader() . $code . '</table>';
191 // Create section to output:
192 $theOutput .= $this->pObj->doc->section('', $code, 0, 1);
193 } else {
194 $theOutput .= $this->pObj->doc->section('', '<br /><br />' . $this->pObj->doc->icons(1) . 'There were no indexed pages found in the tree.<br /><br />', 0, 1);
195 }
196 return $theOutput;
197 }
198
199 /**
200 * Create information table row for a page regarding indexing information.
201 *
202 * @param array Data array for this page
203 * @param string HTML content for first column (page tree icon etc.)
204 * @return string HTML code. (table row)
205 */
206 public function indexed_info($data, $firstColContent) {
207 // Query:
208 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('ISEC.phash_t3, ISEC.rl0, ISEC.rl1, ISEC.rl2, ISEC.page_id, ISEC.uniqid, ' . 'IP.phash, IP.phash_grouping, IP.cHashParams, IP.data_filename, IP.data_page_id, ' . 'IP.data_page_reg1, IP.data_page_type, IP.data_page_mp, IP.gr_list, IP.item_type, ' . 'IP.item_title, IP.item_description, IP.item_mtime, IP.tstamp, IP.item_size, ' . 'IP.contentHash, IP.crdate, IP.parsetime, IP.sys_language_uid, IP.item_crdate, ' . 'IP.externalUrl, IP.recordUid, IP.freeIndexUid, IP.freeIndexSetId, count(*) AS count_val', 'index_phash IP, index_section ISEC', 'IP.phash = ISEC.phash AND ISEC.page_id = ' . (int)$data['uid'], 'IP.phash,IP.phash_grouping,IP.cHashParams,IP.data_filename,IP.data_page_id,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2,ISEC.page_id,ISEC.uniqid,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId', 'IP.item_type, IP.tstamp', $this->maxListPerPage + 1);
209 // Initialize variables:
210 $rowCount = 0;
211 $lines = array();
212 // Collecting HTML rows here.
213 $phashAcc = array();
214 // Collecting phash values (to remove local indexing for)
215 $phashAcc[] = 0;
216 // Traverse the result set of phash rows selected:
217 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
218 if ($rowCount == $this->maxListPerPage) {
219 $rowCount++;
220 // Increase to the extra warning row will appear as well.
221 break;
222 }
223 // Adds a display row:
224 $lines[$row['phash_grouping']][] = $this->printPhashRow($row, isset($lines[$row['phash_grouping']]), $this->getGrListEntriesForPhash($row['phash'], $row['gr_list']));
225 $rowCount++;
226 $phashAcc[] = $row['phash'];
227 $this->allPhashListed[] = $row['phash'];
228 }
229 // Compile rows into the table:
230 $out = '';
231 $cellAttrib = $data['_CSSCLASS'] ? ' class="' . $data['_CSSCLASS'] . '"' : '';
232 if (count($lines)) {
233 $firstColContent = '<td rowspan="' . $rowCount . '"' . $cellAttrib . '>' . $firstColContent . '</td>';
234 foreach ($lines as $rowSet) {
235 foreach ($rowSet as $rows) {
236 $out .= '
237 <tr class="bgColor-20">' . $firstColContent . implode('', $rows) . '</tr>';
238 $firstColContent = '';
239 }
240 }
241 if ($rowCount > $this->maxListPerPage) {
242 // Now checking greater than, because we increased $rowCount before...
243 $out .= '
244 <tr class="bgColor-20">
245 <td>&nbsp;</td>
246 <td colspan="' . ($this->returnNumberOfColumns() - 1) . '">' . $this->pObj->doc->icons(3) . '<span class="">There were more than ' . $this->maxListPerPage . ' rows. <a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_info', array('id' => $this->pObj->id, 'listAll' => 1))) . '">Click here to list them ALL!</a></span></td>
247 </tr>';
248 }
249 } else {
250 $out .= '
251 <tr class="bgColor-20">
252 <td' . $cellAttrib . '>' . $firstColContent . '</td>
253 <td colspan="' . ($this->returnNumberOfColumns() - 1) . '"><em>Not indexed</em></td>
254 </tr>';
255 }
256 // Checking for phash-rows which are NOT joined with the section table:
257 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('IP.*', 'index_phash IP', 'IP.data_page_id = ' . (int)$data['uid'] . ' AND IP.phash NOT IN (' . implode(',', $phashAcc) . ')');
258 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
259 $out .= '
260 <tr class="typo3-red">
261 <td colspan="' . $this->returnNumberOfColumns() . '"><strong>Warning:</strong> phash-row "' . $row['phash'] . '" didn\'t have a representation in the index_section table!</td>
262 </tr>';
263 $this->allPhashListed[] = $row['phash'];
264 }
265 return $out;
266 }
267
268 /**
269 * Render a single row of information about a indexing entry.
270 *
271 * @param array Row from query (combined phash table with sections etc).
272 * @param bool Set if grouped to previous result; the icon of the element is not shown again.
273 * @param array Array of index_grlist records.
274 * @return array Array of table rows.
275 * @see indexed_info()
276 */
277 public function printPhashRow($row, $grouping = 0, $extraGrListRows) {
278 $lines = array();
279 // Title cell attributes will highlight TYPO3 pages with a slightly darker color (bgColor4) than attached medias. Also IF there are more than one section record for a phash row it will be red as a warning that something is wrong!
280 $titleCellAttribs = $row['count_val'] != 1 ? ' bgcolor="red"' : ($row['item_type'] === '0' ? ' class="bgColor4"' : '');
281 if ($row['item_type']) {
282 $arr = unserialize($row['cHashParams']);
283 $page = $arr['key'] ? ' [' . $arr['key'] . ']' : '';
284 } else {
285 $page = '';
286 }
287 $elTitle = $this->linkDetails($row['item_title'] ? htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['item_title'], 20) . $page) : '<em>[No Title]</em>', $row['phash']);
288 $cmdLinks = $this->printRemoveIndexed($row['phash'], 'Clear phash-row') . $this->printReindex($row, 'Re-index element');
289 switch ($this->pObj->MOD_SETTINGS['type']) {
290 case 1:
291 // Technical details:
292 // Display icon:
293 if (!$grouping) {
294 $lines[] = '<td>' . $this->makeItemTypeIcon($row['item_type'], ($row['data_filename'] ? $row['data_filename'] : $row['item_title'])) . '</td>';
295 } else {
296 $lines[] = '<td>&nbsp;</td>';
297 }
298 // Title displayed:
299 $lines[] = '<td' . $titleCellAttribs . '>' . $elTitle . '</td>';
300 // Remove-indexing-link:
301 $lines[] = '<td>' . $cmdLinks . '</td>';
302 // Various data:
303 $lines[] = '<td>' . $row['phash'] . '</td>';
304 $lines[] = '<td>' . $row['contentHash'] . '</td>';
305 if ($row['item_type'] === '0') {
306 $lines[] = '<td>' . ($row['data_page_id'] ? $row['data_page_id'] : '&nbsp;') . '</td>';
307 $lines[] = '<td>' . ($row['data_page_type'] ? $row['data_page_type'] : '&nbsp;') . '</td>';
308 $lines[] = '<td>' . ($row['sys_language_uid'] ? $row['sys_language_uid'] : '&nbsp;') . '</td>';
309 $lines[] = '<td>' . ($row['data_page_mp'] ? $row['data_page_mp'] : '&nbsp;') . '</td>';
310 } else {
311 $lines[] = '<td colspan="4">' . htmlspecialchars($row['data_filename']) . '</td>';
312 }
313 $lines[] = '<td>' . $row['gr_list'] . $this->printExtraGrListRows($extraGrListRows) . '</td>';
314 $lines[] = '<td>' . $this->printRootlineInfo($row) . '</td>';
315 $lines[] = '<td>' . ($row['page_id'] ? $row['page_id'] : '&nbsp;') . '</td>';
316 $lines[] = '<td>' . ($row['phash_t3'] != $row['phash'] ? $row['phash_t3'] : '&nbsp;') . '</td>';
317 $lines[] = '<td>' . ($row['freeIndexUid'] ? $row['freeIndexUid'] . ($row['freeIndexSetId'] ? '/' . $row['freeIndexSetId'] : '') : '&nbsp;') . '</td>';
318 $lines[] = '<td>' . ($row['recordUid'] ? $row['recordUid'] : '&nbsp;') . '</td>';
319 // cHash parameters:
320 $arr = unserialize($row['cHashParams']);
321 if (!is_array($arr)) {
322 $arr = array(
323 'cHash' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_general.xlf:LGL.error', TRUE)
324 );
325 }
326 $theCHash = $arr['cHash'];
327 unset($arr['cHash']);
328 if ($row['item_type']) {
329 // pdf...
330 $lines[] = '<td>' . ($arr['key'] ? 'Page ' . $arr['key'] : '') . '&nbsp;</td>';
331 } elseif ($row['item_type'] == 0) {
332 $lines[] = '<td>' . htmlspecialchars(GeneralUtility::implodeArrayForUrl('', $arr)) . '&nbsp;</td>';
333 } else {
334 $lines[] = '<td class="bgColor">&nbsp;</td>';
335 }
336 $lines[] = '<td>' . $theCHash . '</td>';
337 break;
338 case 2:
339 // Words and content:
340 // Display icon:
341 if (!$grouping) {
342 $lines[] = '<td>' . $this->makeItemTypeIcon($row['item_type'], ($row['data_filename'] ? $row['data_filename'] : $row['item_title'])) . '</td>';
343 } else {
344 $lines[] = '<td>&nbsp;</td>';
345 }
346 // Title displayed:
347 $lines[] = '<td' . $titleCellAttribs . '>' . $elTitle . '</td>';
348 // Remove-indexing-link:
349 $lines[] = '<td>' . $cmdLinks . '</td>';
350 // Query:
351 $ftrow = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'index_fulltext', 'phash = ' . (int)$row['phash']);
352 $lines[] = '<td style="white-space: normal;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($ftrow['fulltextdata'], 3000)) . '<hr/><em>Size: ' . strlen($ftrow['fulltextdata']) . '</em>' . '</td>';
353 // Query:
354 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_words.baseword, index_rel.*', 'index_rel, index_words', 'index_rel.phash = ' . (int)$row['phash'] . ' AND index_words.wid = index_rel.wid', '', '', '', 'baseword');
355 $wordList = '';
356 if (is_array($ftrows)) {
357 $indexed_words = array_keys($ftrows);
358 sort($indexed_words);
359 $wordList = htmlspecialchars(implode(' ', $indexed_words));
360 $wordList .= '<hr/><em>Count: ' . count($indexed_words) . '</em>';
361 }
362 $lines[] = '<td style="white-space: normal;">' . $wordList . '</td>';
363 break;
364 default:
365 // Overview
366 // Display icon:
367 if (!$grouping) {
368 $lines[] = '<td>' . $this->makeItemTypeIcon($row['item_type'], ($row['data_filename'] ? $row['data_filename'] : $row['item_title'])) . '</td>';
369 } else {
370 $lines[] = '<td>&nbsp;</td>';
371 }
372 // Title displayed:
373 $lines[] = '<td' . $titleCellAttribs . '>' . $elTitle . '</td>';
374 // Remove-indexing-link:
375 $lines[] = '<td>' . $cmdLinks . '</td>';
376 $lines[] = '<td style="white-space: normal;">' . htmlspecialchars($row['item_description']) . '...</td>';
377 $lines[] = '<td>' . GeneralUtility::formatSize($row['item_size']) . '</td>';
378 $lines[] = '<td>' . BackendUtility::dateTimeAge($row['tstamp']) . '</td>';
379 }
380 return $lines;
381 }
382
383 /**
384 * Creates the header row for the table
385 *
386 * @return string HTML string (table row)
387 */
388 public function printPhashRowHeader() {
389 $lines = array();
390 switch ($this->pObj->MOD_SETTINGS['type']) {
391 case 1:
392 $lines[] = '<td>&nbsp;</td>';
393 $lines[] = '<td>&nbsp;</td>';
394 $lines[] = '<td>Title</td>';
395 $lines[] = '<td bgcolor="red">' . $this->printRemoveIndexed('ALL', 'Clear ALL phash-rows below!') . '</td>';
396 $lines[] = '<td>pHash</td>';
397 $lines[] = '<td>contentHash</td>';
398 $lines[] = '<td>&amp;id</td>';
399 $lines[] = '<td>&amp;type</td>';
400 $lines[] = '<td>&amp;L</td>';
401 $lines[] = '<td>&amp;MP</td>';
402 $lines[] = '<td>grlist</td>';
403 $lines[] = '<td>Rootline</td>';
404 $lines[] = '<td>page_id</td>';
405 $lines[] = '<td>phash_t3</td>';
406 $lines[] = '<td>CfgUid</td>';
407 $lines[] = '<td>RecUid</td>';
408 $lines[] = '<td>GET-parameters</td>';
409 $lines[] = '<td>&amp;cHash</td>';
410 break;
411 case 2:
412 $lines[] = '<td>&nbsp;</td>';
413 $lines[] = '<td>&nbsp;</td>';
414 $lines[] = '<td>Title</td>';
415 $lines[] = '<td bgcolor="red">' . $this->printRemoveIndexed('ALL', 'Clear ALL phash-rows below!') . '</td>';
416 $lines[] = '<td>Content<br />
417 <img src="clear.gif" width="300" height="1" alt="" /></td>';
418 $lines[] = '<td>Words<br />
419 <img src="clear.gif" width="300" height="1" alt="" /></td>';
420 break;
421 default:
422 $lines[] = '<td>&nbsp;</td>';
423 $lines[] = '<td>&nbsp;</td>';
424 $lines[] = '<td>Title</td>';
425 $lines[] = '<td bgcolor="red">' . $this->printRemoveIndexed('ALL', 'Clear ALL phash-rows below!') . '</td>';
426 $lines[] = '<td>Description</td>';
427 $lines[] = '<td>Size</td>';
428 $lines[] = '<td>Indexed:</td>';
429 }
430 $out = '<tr class="tableheader bgColor5">' . implode('', $lines) . '</tr>';
431 return $out;
432 }
433
434 /**
435 * Returns the number of columns depending on display type of list
436 *
437 * @return int Number of columns in list:
438 */
439 public function returnNumberOfColumns() {
440 switch ($this->pObj->MOD_SETTINGS['type']) {
441 case 1:
442 return 18;
443 break;
444 case 2:
445 return 6;
446 break;
447 default:
448 return 7;
449 }
450 }
451
452 /*******************************
453 *
454 * Details display, phash row
455 *
456 *******************************/
457 /**
458 * Showing details for a particular phash row
459 *
460 * @param int phash value to display details for.
461 * @return string HTML content
462 */
463 public function showDetailsForPhash($phash) {
464 $content = '';
465 $phash = (int)$phash;
466 // Selects the result row:
467 $phashRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'index_phash', 'phash = ' . $phash);
468 // If found, display:
469 if (is_array($phashRecord)) {
470 $content .= '<h4>phash row content:</h4>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($phashRecord);
471 // Getting debug information if any:
472 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'index_debug', 'phash = ' . $phash);
473 if (is_array($ftrows)) {
474 $debugInfo = unserialize($ftrows[0]['debuginfo']);
475 $lexer = $debugInfo['lexer'];
476 unset($debugInfo['lexer']);
477 $content .= '<h3>Debug information:</h3>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($debugInfo);
478 $content .= '<h4>Debug information / lexer splitting:</h4>' . '<hr/><strong>' . $lexer . '</strong><hr/>';
479 }
480 $content .= '<h3>Word statistics</h3>';
481 // Finding all words for this phash:
482 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_words.*, index_rel.*', 'index_rel, index_words', 'index_rel.phash = ' . $phash . ' AND index_words.wid = index_rel.wid', '', 'index_words.baseword', '');
483 $pageRec = BackendUtility::getRecord('pages', $phashRecord['data_page_id']);
484 $showStopWordCheckBox = $GLOBALS['BE_USER']->isAdmin();
485 $content .= $this->listWords($ftrows, 'All words found on page (' . count($ftrows) . '):', $showStopWordCheckBox, $pageRec);
486 if ($this->enableMetaphoneSearch) {
487 // Group metaphone hash:
488 $metaphone = array();
489 foreach ($ftrows as $row) {
490 $metaphone[$row['metaphone']][] = $row['baseword'];
491 }
492 $content .= $this->listMetaphoneStat($metaphone, 'Metaphone stats:');
493 }
494 // Finding top-20 on frequency for this phash:
495 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_words.baseword, index_words.metaphone, index_rel.*', 'index_rel, index_words', 'index_rel.phash = ' . $phash . ' AND index_words.wid = index_rel.wid
496 AND index_words.is_stopword=0', '', 'index_rel.freq DESC', '20');
497 $content .= $this->listWords($ftrows, 'Top-20 words by frequency:', 2);
498 // Finding top-20 on count for this phash:
499 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_words.baseword, index_words.metaphone, index_rel.*', 'index_rel, index_words', 'index_rel.phash = ' . $phash . ' AND index_words.wid = index_rel.wid
500 AND index_words.is_stopword=0', '', 'index_rel.count DESC', '20');
501 $content .= $this->listWords($ftrows, 'Top-20 words by count:', 2);
502 $content .= '<h3>Section records for this phash</h3>';
503 // Finding sections for this record:
504 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'index_section', 'index_section.phash = ' . $phash, '', '', '');
505 $content .= \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($ftrows);
506 // Add go-back link:
507 $content = $this->linkList() . $content . $this->linkList();
508 } else {
509 $content .= 'Error: No phash row found';
510 }
511 return $content;
512 }
513
514 /**
515 * Create table with list of words from $ftrows
516 *
517 * @param array Array of records selected from index_rel/index_words
518 * @param string Header string to show before table.
519 * @param bool If set, the stopWord checkboxes will be shown in the word list. Only for admins. (because it is a global setting, not per-site).
520 * @param array The page record from which to load the keywords, if any.
521 * @return string HTML table
522 */
523 public function listWords($ftrows, $header, $stopWordBoxes = FALSE, $page = '') {
524 // Prepare keywords:
525 $keywords = is_array($page) ? array_flip(GeneralUtility::trimExplode(',', $page['keywords'], TRUE)) : '';
526 // Render list:
527 $trows = '';
528 $trows .= '
529 <tr class="tableheader bgColor5">
530 ' . ($stopWordBoxes ? '<td>' . htmlspecialchars('Stopword:') . '</td>' : '') . '
531 <td>' . htmlspecialchars('Word:') . '</td>
532 <td>' . htmlspecialchars('Count:') . '</td>
533 <td>' . htmlspecialchars('First:') . '</td>
534 <td>' . htmlspecialchars('Frequency:') . '</td>
535 <td>' . htmlspecialchars('Flags:') . '</td>
536 ' . (is_array($keywords) ? '<td>' . htmlspecialchars('Page keyword:') . '</td>' : '') . '
537 </tr>
538 ';
539 foreach ($ftrows as $row) {
540 $hiddenField = $stopWordBoxes != 2 ? '<input type="hidden" name="stopWord[' . $row['wid'] . ']" value="0" />' : '';
541 $trows .= '
542 <tr class="' . ($row['is_stopword'] ? 'bgColor' : 'bgColor4') . '">
543 ' . ($stopWordBoxes ? '<td align="center"' . ($row['is_stopword'] ? ' style="background-color:red;"' : '') . '>' . $hiddenField . '<input type="checkbox" name="stopWord[' . $row['wid'] . ']" value="1"' . ($row['is_stopword'] ? 'checked="checked"' : '') . ' /></td>' : '') . '
544 <td>' . $this->linkWordDetails(htmlspecialchars($row['baseword']), $row['wid']) . '</td>
545 <td>' . htmlspecialchars($row['count']) . '</td>
546 <td>' . htmlspecialchars($row['first']) . '</td>
547 <td>' . htmlspecialchars($row['freq']) . '</td>
548 <td>' . htmlspecialchars($this->flagsMsg($row['flags'])) . '</td>
549 ' . (is_array($keywords) ? '<td align="center"' . (isset($keywords[$row['baseword']]) ? ' class="bgColor2"' : '') . '><input type="hidden" name="pageKeyword[' . $row['baseword'] . ']" value="0" /><input type="checkbox" name="pageKeyword[' . $row['baseword'] . ']" value="1"' . (isset($keywords[$row['baseword']]) ? 'checked="checked"' : '') . ' /></td>' : '') . '
550 </tr>
551 ';
552 }
553 return '<h4>' . htmlspecialchars($header) . '</h4>' . '
554 <table border="0" cellspacing="1" cellpadding="2" class="c-list">
555 ' . $trows . '
556 </table>' . ($stopWordBoxes ? '<input type="submit" value="Change stop-word settings" name="_stopwords" onclick="document.webinfoForm.action=\'' . htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';" />' : '') . (is_array($keywords) ? '<input type="submit" value="Set page keywords" name="_pageKeywords" onclick="document.webinfoForm.action=\'' . htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI')) . '\';" /><input type="hidden" name="pageKeyword_pageUid" value="' . $page['uid'] . '" />' . '<br />Current keywords are: <em>' . htmlspecialchars(implode(', ', array_keys($keywords))) . '</em>' : '');
557 }
558
559 /**
560 * Displays table of metaphone groups larger than 1
561 *
562 * @param array Result from word selection (index_rel/index_words)
563 * @param string Header string
564 * @return string HTML table
565 */
566 public function listMetaphoneStat($ftrows, $header) {
567 $trows = '';
568 $trows .= '
569 <tr class="tableheader bgColor5">
570 <td>' . htmlspecialchars('Metaphone:') . '</td>
571 <td>' . htmlspecialchars('Hash:') . '</td>
572 <td>' . htmlspecialchars('Count:') . '</td>
573 <td>' . htmlspecialchars('Words:') . '</td>
574 </tr>
575 ';
576 foreach ($ftrows as $metaphone => $words) {
577 if (count($words) > 1) {
578 $trows .= '
579 <tr class="bgColor4">
580 <td>' . $this->linkMetaPhoneDetails($this->indexerObj->metaphone($words[0], 1), $metaphone) . '</td>
581 <td>' . htmlspecialchars($metaphone) . '</td>
582 <td>' . htmlspecialchars(count($words)) . '</td>
583 <td style="white-space: normal;">' . htmlspecialchars(implode(', ', $words)) . '</td>
584 </tr>
585 ';
586 }
587 }
588 return '<h4>' . htmlspecialchars($header) . '</h4>' . '<table border="0" cellspacing="1" cellpadding="2" class="c-list">
589 ' . $trows . '
590 </table>';
591 }
592
593 /**
594 * Wraps input string in a link that will display details for the word. Eg. which other pages has the word, metaphone associations etc.
595 *
596 * @param string String to wrap, possibly a title or so.
597 * @param int wid value to show details for
598 * @return string Wrapped string
599 */
600 public function linkWordDetails($string, $wid) {
601 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('wid' => $wid, 'phash' => ''))) . '">' . $string . '</a>';
602 }
603
604 /**
605 * Wraps input string in a link to see more details for metaphone value
606 *
607 * @param string String to wrap
608 * @param int Metaphone value
609 * @return string Wrapped string
610 */
611 public function linkMetaPhoneDetails($string, $metaphone) {
612 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('metaphone' => $metaphone, 'wid' => '', 'phash' => ''))) . '">' . $string . '</a>';
613 }
614
615 /**
616 * Creates message for flag value
617 *
618 * @param int Flags integer
619 * @return string Message string
620 */
621 public function flagsMsg($flags) {
622 if ($flags > 0) {
623 return ($flags & 128 ? '<title>' : '') . ($flags & 64 ? '<meta/keywords>' : '') . ($flags & 32 ? '<meta/description>' : '') . ' (' . $flags . ')';
624 }
625 }
626
627 /*******************************
628 *
629 * Details display, words / metaphone
630 *
631 *******************************/
632 /**
633 * Show details for words
634 *
635 * @param int Word ID (wid)
636 * @return string HTML content
637 */
638 public function showDetailsForWord($wid) {
639 // Select references to this word
640 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_phash.*, index_section.*, index_rel.*', 'index_rel, index_section, index_phash', 'index_rel.wid = ' . (int)$wid . ' AND index_rel.phash = index_section.phash' . ' AND index_section.phash = index_phash.phash', '', 'index_rel.freq DESC', '');
641 // Headers:
642 $content = '
643 <tr class="tableheader bgColor5">
644 <td>phash</td>
645 <td>page_id</td>
646 <td>data_filename</td>
647 <td>count</td>
648 <td>first</td>
649 <td>freq</td>
650 <td>flags</td>
651 </tr>';
652 if (is_array($ftrows)) {
653 foreach ($ftrows as $wDat) {
654 $content .= '
655 <tr class="bgColor4">
656 <td>' . $this->linkDetails(htmlspecialchars($wDat['phash']), $wDat['phash']) . '</td>
657 <td>' . htmlspecialchars($wDat['page_id']) . '</td>
658 <td>' . htmlspecialchars($wDat['data_filename']) . '</td>
659 <td>' . htmlspecialchars($wDat['count']) . '</td>
660 <td>' . htmlspecialchars($wDat['first']) . '</td>
661 <td>' . htmlspecialchars($wDat['freq']) . '</td>
662 <td>' . htmlspecialchars($wDat['flags']) . '</td>
663 </tr>';
664 }
665 }
666 // Compile table:
667 $content = '
668 <table border="0" cellspacing="1" cellpadding="2" class="c-list">' . $content . '
669 </table>';
670 // Add go-back link:
671 $content = $content . $this->linkList();
672 return $content;
673 }
674
675 /**
676 * Show details for metaphone value
677 *
678 * @param int Metaphone integer hash
679 * @return string HTML content
680 */
681 public function showDetailsForMetaphone($metaphone) {
682 // Finding top-20 on frequency for this phash:
683 $ftrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('index_words.*', 'index_words', 'index_words.metaphone = ' . (int)$metaphone, '', 'index_words.baseword', '');
684 if (count($ftrows)) {
685 $content .= '<h4>Metaphone: ' . $this->indexerObj->metaphone($ftrows[0]['baseword'], 1) . '</h4>';
686 $content .= '
687 <tr class="tableheader bgColor5">
688 <td>Word</td>
689 <td>Is stopword?</td>
690 </tr>';
691 if (is_array($ftrows)) {
692 foreach ($ftrows as $wDat) {
693 $content .= '
694 <tr class="bgColor4">
695 <td>' . $this->linkWordDetails(htmlspecialchars($wDat['baseword']), $wDat['wid']) . '</td>
696 <td>' . htmlspecialchars(($wDat['is_stopword'] ? 'YES' : 'No')) . '</td>
697 </tr>';
698 }
699 }
700 $content = '
701 <table border="0" cellspacing="1" cellpadding="2" class="c-list">' . $content . '
702 </table>';
703 if ($this->indexerObj->metaphone($ftrows[0]['baseword']) != $metaphone) {
704 $content .= 'ERROR: Metaphone string and hash did not match for some reason!?';
705 }
706 // Add go-back link:
707 $content = $content . $this->linkList();
708 }
709 return $content;
710 }
711
712 /*******************************
713 *
714 * Helper functions
715 *
716 *******************************/
717 /**
718 * Creates icon which clears indexes for a certain list of phash values.
719 *
720 * @param string List of phash integers
721 * @param string Alt-text for the garbage bin icon.
722 * @return string HTML img-tag with link around.
723 */
724 public function printRemoveIndexed($phash, $alt) {
725 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('deletePhash' => $phash))) . '" title="' . htmlspecialchars($alt) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
726 }
727
728 /**
729 * Button for re-indexing of documents
730 *
731 * @param array phash table result row.
732 * @param string Title attribute text for icon
733 * @return string HTML content; Icon wrapped in link.
734 */
735 public function printReindex($resultRow, $alt) {
736 if ($resultRow['item_type'] && $resultRow['item_type'] !== '0') {
737 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('reindex' => $resultRow['phash'], 'reindex_id' => $resultRow['page_id']))) . '">' . '<img ' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($GLOBALS['BACK_PATH'], 'gfx/refresh_n.gif', 'width="14" height="14"') . ' hspace="1" vspace="2" border="0" title="' . htmlspecialchars($alt) . '" alt="" />' . '</a>';
738 }
739 }
740
741 /**
742 * Wraps input string in a link that will display details for the phash value set.
743 *
744 * @param string String to wrap, possibly a title or so.
745 * @param int phash value to show details for
746 * @return string Wrapped string
747 */
748 public function linkDetails($string, $phash) {
749 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('phash' => $phash))) . '">' . $string . '</a>';
750 }
751
752 /**
753 * Creates link back to listing
754 *
755 * @return string Link back to list
756 */
757 public function linkList() {
758 return '<br /><a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_info', array('id' => $this->pObj->id))) . '">Back to list.</a><br />';
759 }
760
761 /**
762 * Wraps input string in a link that will display details for the phash value set.
763 *
764 * @param string String to wrap, possibly a title or so.
765 * @param int phash value to show details for
766 * @return string Wrapped string
767 */
768 public function showPageDetails($string, $id) {
769 return '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_info', array('id' => $id, 'SET' => array('depth' => 0, 'type' => 1)))) . '">' . $string . '</a>';
770 }
771
772 /**
773 * Prints the gr_lists attached to a indexed entry.
774 *
775 * @param array Array of index_grlist records
776 * @return string HTML code.
777 */
778 public function printExtraGrListRows($extraGrListRows) {
779 if (count($extraGrListRows)) {
780 $lines = array();
781 foreach ($extraGrListRows as $r) {
782 $lines[] = $r['gr_list'];
783 }
784 return '<br /><span class="text-muted">' . implode('<br />', $lines) . '</span>';
785 }
786 }
787
788 /**
789 * Print path for indexing
790 *
791 * @param array Result row with content from index_section
792 * @return string Rootline information
793 */
794 public function printRootlineInfo($row) {
795 $uidCollection = array();
796 if ($row['rl0']) {
797 $uidCollection[0] = $row['rl0'];
798 if ($row['rl1']) {
799 $uidCollection[1] = $row['rl1'];
800 if ($row['rl2']) {
801 $uidCollection[2] = $row['rl2'];
802 // Additional levels:
803 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'])) {
804 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel) {
805 if ($row[$fieldName]) {
806 $uidCollection[$rootLineLevel] = $row[$fieldName];
807 }
808 }
809 }
810 }
811 }
812 }
813 // Return root line.
814 ksort($uidCollection);
815 return implode('/', $uidCollection);
816 }
817
818 /**
819 * Return icon for file extension
820 *
821 * @param string File extension / item type
822 * @param string Title attribute value in icon.
823 * @return string <img> tag for icon
824 */
825 public function makeItemTypeIcon($it, $alt = '') {
826 if (!isset($this->iconFileNameCache[$it])) {
827 if ($it === '0') {
828 $icon = 'EXT:indexed_search/pi/res/pages.gif';
829 } elseif ($this->external_parsers[$it]) {
830 $icon = $this->external_parsers[$it]->getIcon($it);
831 }
832 $fullPath = GeneralUtility::getFileAbsFileName($icon);
833 if ($fullPath) {
834 $info = @getimagesize($fullPath);
835 $iconPath = $GLOBALS['BACK_PATH'] . '../' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($fullPath);
836 $this->iconFileNameCache[$it] = is_array($info) ? '<img src="' . $iconPath . '" ' . $info[3] . ' title="###TITLE_ATTRIBUTE###" alt="" />' : '';
837 }
838 }
839 return str_replace('###TITLE_ATTRIBUTE###', htmlspecialchars($it . ': ' . $alt), $this->iconFileNameCache[$it]);
840 }
841
842 /********************************
843 *
844 * Reindexing
845 *
846 *******************************/
847 /**
848 * Re-indexing files/records attached to a page.
849 *
850 * @param int Phash value
851 * @param int The page uid for the section record (file/url could appear more than one place you know...)
852 * @return string HTML content
853 */
854 public function reindexPhash($phash, $pageId) {
855 // Query:
856 $resultRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('ISEC.*, IP.*', 'index_phash IP, index_section ISEC', 'IP.phash = ISEC.phash
857 AND IP.phash = ' . (int)$phash . '
858 AND ISEC.page_id = ' . (int)$pageId);
859 $content = '';
860 if (is_array($resultRow)) {
861 if ($resultRow['item_type'] && $resultRow['item_type'] !== '0') {
862 // (Re)-Indexing file on page.
863 $indexerObj = GeneralUtility::makeInstance(\TYPO3\CMS\IndexedSearch\Indexer::class);
864 $indexerObj->backend_initIndexer($pageId, 0, 0, '', $this->getUidRootLineForClosestTemplate($pageId));
865 // URL or local file:
866 if ($resultRow['externalUrl']) {
867 $indexerObj->indexExternalUrl($resultRow['data_filename']);
868 } else {
869 $indexerObj->indexRegularDocument($resultRow['data_filename'], TRUE);
870 }
871 if ($indexerObj->file_phash_arr['phash'] != $resultRow['phash']) {
872 $content .= 'ERROR: phash (' . $indexerObj->file_phash_arr['phash'] . ') did NOT match ' . $resultRow['phash'] . ' for strange reasons!';
873 }
874 $content .= '<h4>Log for re-indexing of "' . htmlspecialchars($resultRow['data_filename']) . '":</h4>';
875 $content .= \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($indexerObj->internal_log);
876 $content .= '<h4>Hash-array, page:</h4>';
877 $content .= \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($indexerObj->hash);
878 $content .= '<h4>Hash-array, file:</h4>';
879 $content .= \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($indexerObj->file_phash_arr);
880 }
881 }
882 // Link back to list.
883 $content .= $this->linkList();
884 return $content;
885 }
886
887 /**
888 * Get rootline for closest TypoScript template root.
889 * Algorithm same as used in Web > Template, Object browser
890 *
891 * @param int The page id to traverse rootline back from
892 * @return array Array where the root lines uid values are found.
893 */
894 public function getUidRootLineForClosestTemplate($id) {
895 $tmpl = GeneralUtility::makeInstance(\TYPO3\CMS\Core\TypoScript\ExtendedTemplateService::class);
896 // Defined global here!
897 $tmpl->tt_track = 0;
898 // Do not log time-performance information
899 $tmpl->init();
900 // Gets the rootLine
901 $sys_page = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Page\PageRepository::class);
902 $rootLine = $sys_page->getRootLine($id);
903 $tmpl->runThroughTemplates($rootLine, 0);
904 // This generates the constants/config + hierarchy info for the template.
905 // Root line uids
906 $rootline_uids = array();
907 foreach ($tmpl->rootLine as $rlkey => $rldat) {
908 $rootline_uids[$rlkey] = $rldat['uid'];
909 }
910 return $rootline_uids;
911 }
912
913 /********************************
914 *
915 * SQL functions
916 *
917 *******************************/
918 /**
919 * Removes ALL data regarding a certain list of indexed phash-row
920 *
921 * @param string List of phash integers
922 * @param bool If set, page cache is cleared as well.
923 * @return void
924 */
925 public function removeIndexedPhashRow($phashList, $clearPageCache = 1) {
926 // @todo This is only a workaround
927 if ($phashList == 'ALL') {
928 $this->drawTableOfIndexedPages();
929 $phashRows = $this->allPhashListed;
930 $this->allPhashListed = array();
931 } else {
932 $phashRows = GeneralUtility::trimExplode(',', $phashList, TRUE);
933 }
934 foreach ($phashRows as $phash) {
935 $phash = (int)$phash;
936 if ($phash > 0) {
937 if ($clearPageCache) {
938 // Clearing page cache:
939 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('page_id', 'index_section', 'phash=' . (int)$phash);
940 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
941 $idList = array();
942 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
943 $idList[] = (int)$row['page_id'];
944 }
945 $pageCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_pages');
946 foreach ($idList as $pageId) {
947 $pageCache->flushByTag('pageId_' . $pageId);
948 }
949 }
950 }
951 // Removing old registrations for all tables.
952 $tableArr = explode(',', 'index_phash,index_rel,index_section,index_grlist,index_fulltext,index_debug');
953 foreach ($tableArr as $table) {
954 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'phash=' . (int)$phash);
955 }
956 }
957 }
958 }
959
960 /**
961 * Returns an array with gr_list records for a phash
962 *
963 * @param int phash integer to look up on
964 * @param string gr_list string to filter OUT of the result (first occurence)
965 * @return array Array of records from index_grlist table
966 */
967 public function getGrListEntriesForPhash($phash, $gr_list) {
968 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'index_grlist', 'phash=' . (int)$phash);
969 $lines = array();
970 $isRemoved = 0;
971 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
972 if (!$isRemoved && (string)$row['gr_list'] === (string)$gr_list) {
973 $isRemoved = 1;
974 } else {
975 $lines[] = $row;
976 }
977 }
978 return $lines;
979 }
980
981 /**
982 * Setting / Unsetting stopwords
983 *
984 * @param array Array of stop-words WIDs with 0/1 to set / unset
985 * @return void
986 */
987 public function processStopWords($stopWords) {
988 if ($GLOBALS['BE_USER']->isAdmin()) {
989 // Traverse words
990 foreach ($stopWords as $wid => $state) {
991 $fieldArray = array(
992 'is_stopword' => $state
993 );
994 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_words', 'wid=' . $wid, $fieldArray);
995 }
996 }
997 }
998
999 /**
1000 * Setting / Unsetting keywords in page header
1001 *
1002 * @param array Page keywords as keys in array with value 0 or 1 for set or unset.
1003 * @param int The page uid of the header where the keywords are to be set.
1004 * @return void
1005 */
1006 public function processPageKeywords($pageKeywords, $pageUid) {
1007 // Get pages current keywords
1008 $pageRec = BackendUtility::getRecord('pages', $pageUid);
1009 $keywords = array_flip(GeneralUtility::trimExplode(',', $pageRec['keywords'], TRUE));
1010 // Merge keywords:
1011 foreach ($pageKeywords as $key => $v) {
1012 if ($v) {
1013 $keywords[$key] = 1;
1014 } else {
1015 unset($keywords[$key]);
1016 }
1017 }
1018 // Compile new list:
1019 $data = array();
1020 $data['pages'][$pageUid]['keywords'] = implode(', ', array_keys($keywords));
1021 $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
1022 $tce->stripslashes_values = 0;
1023 $tce->start($data, array());
1024 $tce->process_datamap();
1025 }
1026
1027 }