[TASK] Remove blank line between docblock and the documented element
[Packages/TYPO3.CMS.git] / typo3 / sysext / indexed_search / Classes / Domain / Repository / AdministrationRepository.php
1 <?php
2 namespace TYPO3\CMS\IndexedSearch\Domain\Repository;
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\FrontendBackendUserAuthentication;
17 use TYPO3\CMS\Backend\Tree\View\PageTreeView;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Cache\CacheManager;
21 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
22 use TYPO3\CMS\Core\DataHandling\DataHandler;
23 use TYPO3\CMS\Core\Imaging\Icon;
24 use TYPO3\CMS\Core\Imaging\IconFactory;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Dbal\Database\DatabaseConnection;
27 use TYPO3\CMS\IndexedSearch\FileContentParser;
28
29 /**
30 * Administration repository
31 */
32 class AdministrationRepository
33 {
34 /**
35 * List of fileContentParsers
36 *
37 * @var FileContentParser[]
38 */
39 public $external_parsers = array();
40
41 /**
42 * @var array
43 */
44 protected $allPhashListed = array();
45
46 /**
47 * @var array
48 */
49 protected $iconFileNameCache = array();
50
51 /**
52 * Get group list information
53 *
54 * @param int $phash
55 * @return array
56 */
57 public function getGrlistRecord($phash)
58 {
59 $db = $this->getDatabaseConnection();
60 $res = $db->exec_SELECTquery('index_grlist.*', 'index_grlist', 'phash=' . (int)$phash);
61 $allRows = array();
62 $numberOfRows = $db->sql_num_rows($res);
63 while ($row = $db->sql_fetch_assoc($res)) {
64 $row['pcount'] = $numberOfRows;
65 $allRows[] = $row;
66 }
67 $db->sql_free_result($res);
68 return $allRows;
69 }
70
71 /**
72 * Get number of fulltext records
73 *
74 * @param int $phash
75 * @return int|bool
76 */
77 public function getNumberOfFulltextRecords($phash)
78 {
79 return $this->getDatabaseConnection()->exec_SELECTcountRows('phash', 'index_fulltext', 'phash=' . (int)$phash);
80 }
81
82 /**
83 * Get number of words
84 *
85 * @param int $phash
86 * @return int|bool
87 */
88 public function getNumberOfWords($phash)
89 {
90 return $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'index_rel', 'phash=' . (int)$phash);
91 }
92
93 /**
94 * Get statistic of external documents
95 *
96 * @return array
97 */
98 public function getExternalDocumentsStatistic()
99 {
100 $result = array();
101
102 $db = $this->getDatabaseConnection();
103 $res = $db->exec_SELECTquery(
104 'count(*) AS pcount,index_phash.*',
105 'index_phash',
106 'item_type<>\'0\'',
107 'phash_grouping,phash,cHashParams,data_filename,data_page_id,data_page_reg1,data_page_type,data_page_mp,gr_list,item_type,item_title,item_description,item_mtime,tstamp,item_size,contentHash,crdate,parsetime,sys_language_uid,item_crdate,externalUrl,recordUid,freeIndexUid,freeIndexSetId',
108 'item_type'
109 );
110 while ($row = $db->sql_fetch_assoc($res)) {
111 $this->addAdditionalInformation($row);
112
113 $result[] = $row;
114
115 if ($row['pcount'] > 1) {
116 $res2 = $db->exec_SELECTquery(
117 'index_phash.*',
118 'index_phash',
119 'phash_grouping=' . (int)$row['phash_grouping'] . ' AND phash<>' . (int)$row['phash']
120 );
121 while ($row2 = $db->sql_fetch_assoc($res2)) {
122 $this->addAdditionalInformation($row2);
123 $result[] = $row2;
124 }
125 $db->sql_free_result($res2);
126 }
127 }
128 $db->sql_free_result($res);
129
130 return $result;
131 }
132
133 /**
134 * Get count of the tables used for indexed_search
135 *
136 * @return array
137 */
138 public function getRecordsNumbers()
139 {
140 $tables = array(
141 'index_phash',
142 'index_words',
143 'index_rel',
144 'index_grlist',
145 'index_section',
146 'index_fulltext',
147 );
148 $recordList = array();
149 foreach ($tables as $tableName) {
150 $recordList[$tableName] = $this->getDatabaseConnection()->exec_SELECTcountRows('*', $tableName);
151 }
152 return $recordList;
153 }
154
155 /**
156 * Get hash types
157 *
158 * @return array
159 */
160 public function getPageHashTypes()
161 {
162 $counts = array();
163 $types = array(
164 'html' => 1,
165 'htm' => 1,
166 'pdf' => 2,
167 'doc' => 3,
168 'txt' => 4
169 );
170 $revTypes = array_flip($types);
171 $revTypes[0] = 'TYPO3 page';
172 $db = $this->getDatabaseConnection();
173 $res = $db->exec_SELECTquery('count(*),item_type', 'index_phash', '', 'item_type', 'item_type');
174 while ($row = $db->sql_fetch_row($res)) {
175 $itemType = $row[1];
176 $counts[] = array(
177 'count' => $row[0],
178 'name' => $revTypes[$itemType],
179 'type' => $itemType,
180 'uniqueCount' => $this->countUniqueTypes($itemType),
181 );
182 }
183 $db->sql_free_result($res);
184
185 return $counts;
186 }
187
188 /**
189 * Count unique types
190 *
191 * @param string $itemType
192 * @return int
193 */
194 protected function countUniqueTypes($itemType)
195 {
196 $db = $this->getDatabaseConnection();
197 $res = $db->exec_SELECTquery(
198 'count(*)',
199 'index_phash',
200 'item_type=' . $db->fullQuoteStr($itemType, 'index_phash'),
201 'phash_grouping'
202 );
203 $items = array();
204 while ($row = $db->sql_fetch_row($res)) {
205 $items[] = $row;
206 }
207 $db->sql_free_result($res);
208
209 return count($items);
210 }
211
212 /**
213 * Get number of section records
214 *
215 * @param int $pageHash
216 * @return int
217 */
218 public function getNumberOfSections($pageHash)
219 {
220 return $this->getDatabaseConnection()->exec_SELECTcountRows('phash', 'index_section', 'phash=' . (int)$pageHash);
221 }
222
223 /**
224 * Get page statistic
225 *
226 * @return array
227 */
228 public function getPageStatistic()
229 {
230 $result = array();
231 $db = $this->getDatabaseConnection();
232 $res = $db->exec_SELECTquery(
233 'count(*) AS pcount,index_phash.*',
234 'index_phash',
235 'data_page_id<>0',
236 'phash_grouping,phash,cHashParams,data_filename,data_page_id,data_page_reg1,data_page_type,data_page_mp,gr_list,item_type,item_title,item_description,item_mtime,tstamp,item_size,contentHash,crdate,parsetime,sys_language_uid,item_crdate,externalUrl,recordUid,freeIndexUid,freeIndexSetId',
237 'data_page_id'
238 );
239 while ($row = $db->sql_fetch_assoc($res)) {
240 $this->addAdditionalInformation($row);
241 $result[] = $row;
242
243 if ($row['pcount'] > 1) {
244 $res2 = $db->exec_SELECTquery(
245 'index_phash.*',
246 'index_phash',
247 'phash_grouping=' . (int)$row['phash_grouping'] . ' AND phash<>' . (int)$row['phash']
248 );
249 while ($row2 = $db->sql_fetch_assoc($res2)) {
250 $this->addAdditionalInformation($row2);
251 $result[] = $row2;
252 }
253 $db->sql_free_result($res2);
254 }
255 }
256 $db->sql_free_result($res);
257
258 return $result;
259 }
260
261 /**
262 * Get general statistic
263 *
264 * @param string $additionalWhere
265 * @param int $pageUid
266 * @param int $max
267 * @return array|NULL
268 */
269 public function getGeneralSearchStatistic($additionalWhere, $pageUid, $max = 50)
270 {
271 $queryParts = array(
272 'SELECT' => 'word, COUNT(*) AS c',
273 'FROM' => 'index_stat_word',
274 'WHERE' => sprintf('pageid= %d ' . $additionalWhere, $pageUid),
275 'GROUPBY' => 'word',
276 'ORDERBY' => '',
277 'LIMIT' => (int)$max
278 );
279 $db = $this->getDatabaseConnection();
280 $res = $db->exec_SELECTquery(
281 $queryParts['SELECT'],
282 $queryParts['FROM'],
283 $queryParts['WHERE'],
284 $queryParts['GROUPBY'],
285 $queryParts['ORDERBY'],
286 $queryParts['LIMIT']
287 );
288
289 $count = 0;
290 if ($res) {
291 $count = $db->sql_num_rows($res);
292 }
293
294 $db->sql_free_result($res);
295
296 // exist several statistics for this page?
297 if ($count == 0) {
298 // Limit access to pages of the current site
299 $secureAddWhere = ' AND pageid IN (' . $this->extGetTreeList((int)$pageUid, 100, 0, '1=1') . ') ';
300 $queryParts['WHERE'] = '1=1 ' . $additionalWhere . $secureAddWhere;
301 }
302
303 return $db->exec_SELECTgetRows(
304 $queryParts['SELECT'],
305 $queryParts['FROM'],
306 $queryParts['WHERE'],
307 $queryParts['GROUPBY'],
308 $queryParts['ORDERBY'],
309 $queryParts['LIMIT']
310 );
311 }
312
313 /**
314 * Add additional information to the result row
315 *
316 * @param array $row
317 * @return void
318 */
319 protected function addAdditionalInformation(array &$row)
320 {
321 $grListRec = $this->getGrlistRecord($row['phash']);
322 $unserializedCHashParams = unserialize($row['cHashParams']);
323
324 $row['numberOfWords'] = $this->getNumberOfWords($row['phash']);
325 $row['numberOfSections'] = $this->getNumberOfSections($row['phash']);
326 $row['numberOfFulltext'] = $this->getNumberOfFulltextRecords($row['phash']);
327 $row['cHashParams'] = !empty($unserializedCHashParams) ? $unserializedCHashParams : '';
328 $row['grList'] = $grListRec;
329 }
330
331 /**
332 * Get the page tree by using \TYPO3\CMS\Backend\Tree\View\PageTreeView
333 *
334 * @param int $pageId
335 * @param int $depth
336 * @param string $mode
337 * @return array
338 */
339 public function getTree($pageId, $depth = 4, $mode)
340 {
341 $allLines = array();
342 $pageRecord = BackendUtility::getRecord('pages', (int)$pageId);
343 if (!$pageRecord) {
344 return $allLines;
345 }
346 /** @var PageTreeView $tree */
347 $tree = GeneralUtility::makeInstance(PageTreeView::class);
348 $perms_clause = $this->getBackendUserAuthentication()->getPagePermsClause(1);
349 $tree->init('AND ' . $perms_clause);
350 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
351 $HTML = '<span title="' . htmlspecialchars($pageRecord['title']) . '">' . $iconFactory->getIconForRecord('pages', $pageRecord, Icon::SIZE_SMALL)->render() . '</span>';
352 $tree->tree[] = array(
353 'row' => $pageRecord,
354 'HTML' => $HTML
355 );
356
357 if ($depth > 0) {
358 $tree->getTree((int)$pageId, $depth, '');
359 }
360 $db = $this->getDatabaseConnection();
361 foreach ($tree->tree as $singleLine) {
362 $res = $db->exec_SELECTquery(
363 'ISEC.phash_t3, ISEC.rl0, ISEC.rl1, ISEC.rl2, ISEC.page_id, ISEC.uniqid, ' .
364 'IP.phash, IP.phash_grouping, IP.cHashParams, IP.data_filename, IP.data_page_id, ' .
365 'IP.data_page_reg1, IP.data_page_type, IP.data_page_mp, IP.gr_list, IP.item_type, ' .
366 'IP.item_title, IP.item_description, IP.item_mtime, IP.tstamp, IP.item_size, ' .
367 'IP.contentHash, IP.crdate, IP.parsetime, IP.sys_language_uid, IP.item_crdate, ' .
368 'IP.externalUrl, IP.recordUid, IP.freeIndexUid, IP.freeIndexSetId, count(*) AS count_val',
369 'index_phash IP, index_section ISEC',
370 'IP.phash = ISEC.phash AND ISEC.page_id = ' . (int)$singleLine['row']['uid'],
371 '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',
372 'IP.item_type, IP.tstamp',
373 10 + 1
374 );
375 $lines = array();
376 // Collecting phash values (to remove local indexing for)
377 // Traverse the result set of phash rows selected:
378 while ($row = $db->sql_fetch_assoc($res)) {
379 $this->allPhashListed[] = $row['phash'];
380 // Adds a display row:
381 $row['icon'] = $this->makeItemTypeIcon($row['item_type']);
382 $row['wordCount'] = count($db->exec_SELECTgetRows(
383 'index_words.baseword, index_rel.*',
384 'index_rel, index_words',
385 'index_rel.phash = ' . (int)$row['phash'] . ' AND index_words.wid = index_rel.wid',
386 '',
387 '',
388 '',
389 'baseword'
390 ));
391
392 if ($mode === 'content') {
393 $row['fulltextData'] = $db->exec_SELECTgetSingleRow(
394 '*',
395 'index_fulltext',
396 'phash = ' . $row['phash']);
397 $wordRecords = $db->exec_SELECTgetRows(
398 'index_words.baseword, index_rel.*',
399 'index_rel, index_words',
400 'index_rel.phash = ' . (int)$row['phash'] . ' AND index_words.wid = index_rel.wid',
401 '', '', '', 'baseword');
402 if (is_array($wordRecords)) {
403 $indexed_words = array_keys($wordRecords);
404 sort($indexed_words);
405 $row['allWords'] = $indexed_words;
406 }
407 }
408
409 $lines[] = $row;
410 }
411
412 $singleLine['lines'] = $lines;
413 $allLines[] = $singleLine;
414 }
415
416 return $allLines;
417 }
418
419 /**
420 * Generates a list of Page-uid's from $id.
421 * The only pages excluded from the list are deleted pages.
422 *
423 * @param int $id page id
424 * @param int $depth to traverse down the page tree.
425 * @param int $begin is an optional integer that determines at which level in the tree to start collecting uid's. Zero means 'start right away', 1 = 'next level and out'
426 * @param string $perms_clause
427 * @return string Returns the list with a comma in the end + id itself
428 */
429 protected function extGetTreeList($id, $depth, $begin = 0, $perms_clause)
430 {
431 $list = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class)->extGetTreeList($id, $depth, $begin, $perms_clause);
432
433 if (empty($list)) {
434 $list = $id;
435 } else {
436 $list = rtrim($list, ',') . ',' . $id;
437 }
438
439 return $list;
440 }
441
442 /**
443 * Remove indexed phash row
444 *
445 * @param string $phashList
446 * @param int $pageId
447 * @param int $depth
448 * @return void
449 */
450 public function removeIndexedPhashRow($phashList, $pageId, $depth = 4)
451 {
452 if ($phashList === 'ALL') {
453 $this->getTree($pageId, $depth, '');
454 $phashRows = $this->allPhashListed;
455 $this->allPhashListed = array();
456 } else {
457 $phashRows = GeneralUtility::trimExplode(',', $phashList, true);
458 }
459
460 $db = $this->getDatabaseConnection();
461 foreach ($phashRows as $phash) {
462 $phash = (int)$phash;
463 if ($phash > 0) {
464 $idList = array();
465 $res = $db->exec_SELECTquery('page_id', 'index_section', 'phash=' . $phash);
466 while ($row = $db->sql_fetch_assoc($res)) {
467 $idList[] = (int)$row['page_id'];
468 }
469 $db->sql_free_result($res);
470
471 if (!empty($idList)) {
472 /** @var FrontendInterface $pageCache */
473 $pageCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pages');
474 foreach ($idList as $pageId) {
475 $pageCache->flushByTag('pageId_' . $pageId);
476 }
477 }
478
479 // Removing old registrations for all tables.
480 $tableArr = array('index_phash', 'index_rel', 'index_section', 'index_grlist', 'index_fulltext', 'index_debug');
481 foreach ($tableArr as $table) {
482 $db->exec_DELETEquery($table, 'phash=' . $phash);
483 }
484 }
485 }
486 }
487
488 /**
489 * Save stop words
490 *
491 * @param array $words stop words
492 * @return void
493 */
494 public function saveStopWords(array $words)
495 {
496 foreach ($words as $wid => $state) {
497 $fieldArray = array(
498 'is_stopword' => (int)$state
499 );
500 $this->getDatabaseConnection()->exec_UPDATEquery('index_words', 'wid=' . (int)$wid, $fieldArray);
501 }
502 }
503
504 /**
505 * Save keywords
506 *
507 * @param array $words keywords
508 * @param int $pageId page id
509 * @return void
510 */
511 public function saveKeywords(array $words, $pageId)
512 {
513 // Get pages current keywords
514 $pageRec = BackendUtility::getRecord('pages', $pageId);
515 if (!is_array($pageRec)) {
516 return;
517 }
518 $keywords = array_flip(GeneralUtility::trimExplode(',', $pageRec['keywords'], true));
519 // Merge keywords:
520 foreach ($words as $key => $v) {
521 if ($v) {
522 $keywords[$key] = 1;
523 } else {
524 unset($keywords[$key]);
525 }
526 }
527 // Compile new list:
528 $data = array();
529 $data['pages'][$pageId]['keywords'] = implode(', ', array_keys($keywords));
530 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
531 $dataHandler->stripslashes_values = 0;
532 $dataHandler->start($data, array());
533 $dataHandler->process_datamap();
534 }
535
536 /**
537 * Collect the type icons
538 *
539 * @param string $itemType
540 * @return string
541 */
542 protected function makeItemTypeIcon($itemType)
543 {
544 if (!isset($this->iconFileNameCache[$itemType])) {
545 $icon = '';
546 if ($itemType === '0') {
547 $icon = 'EXT:indexed_search/Resources/Public/Icons/FileTypes/pages.gif';
548 } elseif ($this->external_parsers[$itemType]) {
549 $icon = $this->external_parsers[$itemType]->getIcon($itemType);
550 }
551 $this->iconFileNameCache[$itemType] = $icon;
552 }
553 return $this->iconFileNameCache[$itemType];
554 }
555
556 /**
557 * @return DatabaseConnection
558 */
559 protected function getDatabaseConnection()
560 {
561 return $GLOBALS['TYPO3_DB'];
562 }
563
564 /**
565 * @return BackendUserAuthentication
566 */
567 protected function getBackendUserAuthentication()
568 {
569 return $GLOBALS['BE_USER'];
570 }
571 }