02c91c1dc189514289b2886d22b43203c2a217b2
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Tree / View / PagePositionMap.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tree\View;
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\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
21 use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
22 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
23 use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
24 use TYPO3\CMS\Core\Imaging\Icon;
25 use TYPO3\CMS\Core\Imaging\IconFactory;
26 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Core\Utility\PathUtility;
29 use TYPO3\CMS\Lang\LanguageService;
30
31 /**
32 * Position map class - generating a page tree / content element list which links for inserting (copy/move) of records.
33 * Used for pages / tt_content element wizards of various kinds.
34 */
35 class PagePositionMap
36 {
37 // EXTERNAL, static:
38 /**
39 * @var string
40 */
41 public $moveOrCopy = 'move';
42
43 /**
44 * @var int
45 */
46 public $dontPrintPageInsertIcons = 0;
47
48 // How deep the position page tree will go.
49 /**
50 * @var int
51 */
52 public $depth = 2;
53
54 // Can be set to the sys_language uid to select content elements for.
55 /**
56 * @var string
57 */
58 public $cur_sys_language;
59
60 // INTERNAL, dynamic:
61 // Request uri
62 /**
63 * @var string
64 */
65 public $R_URI = '';
66
67 // Element id.
68 /**
69 * @var string
70 */
71 public $elUid = '';
72
73 // tt_content element uid to move.
74 /**
75 * @var string
76 */
77 public $moveUid = '';
78
79 // Caching arrays:
80 /**
81 * @var array
82 */
83 public $getModConfigCache = [];
84
85 /**
86 * @var array
87 */
88 public $checkNewPageCache = [];
89
90 // Label keys:
91 /**
92 * @var string
93 */
94 public $l_insertNewPageHere = 'insertNewPageHere';
95
96 /**
97 * @var string
98 */
99 public $l_insertNewRecordHere = 'insertNewRecordHere';
100
101 /**
102 * @var string
103 */
104 public $modConfigStr = 'mod.web_list.newPageWiz';
105
106 /**
107 * Page tree implementation class name
108 *
109 * @var string
110 */
111 protected $pageTreeClassName = ElementBrowserPageTreeView::class;
112
113 /**
114 * @var IconFactory
115 */
116 protected $iconFactory;
117
118 /**
119 * Constructor allowing to set pageTreeImplementation
120 *
121 * @param string $pageTreeClassName
122 */
123 public function __construct($pageTreeClassName = null)
124 {
125 if ($pageTreeClassName !== null) {
126 $this->pageTreeClassName = $pageTreeClassName;
127 }
128 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
129 }
130
131 /*************************************
132 *
133 * Page position map:
134 *
135 **************************************/
136 /**
137 * Creates a "position tree" based on the page tree.
138 *
139 * @param int $id Current page id
140 * @param array $pageinfo Current page record.
141 * @param string $perms_clause Page selection permission clause.
142 * @param string $R_URI Current REQUEST_URI
143 * @return string HTML code for the tree.
144 */
145 public function positionTree($id, $pageinfo, $perms_clause, $R_URI)
146 {
147 // Make page tree object:
148 /** @var \TYPO3\CMS\Backend\Tree\View\PageTreeView $pageTree */
149 $pageTree = GeneralUtility::makeInstance($this->pageTreeClassName);
150 $pageTree->init(' AND ' . $perms_clause);
151 $pageTree->addField('pid');
152 // Initialize variables:
153 $this->R_URI = $R_URI;
154 $this->elUid = $id;
155 // Create page tree, in $this->depth levels.
156 $pageTree->getTree($pageinfo['pid'], $this->depth);
157 // Initialize variables:
158 $saveLatestUid = [];
159 $latestInvDepth = $this->depth;
160 // Traverse the tree:
161 $lines = [];
162 foreach ($pageTree->tree as $cc => $dat) {
163 if ($latestInvDepth > $dat['invertedDepth']) {
164 $margin = 'style="margin-left: ' . ($dat['invertedDepth'] * 16 + 9) . 'px;"';
165 $lines[] = '<ul class="list-tree" ' . $margin . '>';
166 }
167 // Make link + parameters.
168 $latestInvDepth = $dat['invertedDepth'];
169 $saveLatestUid[$latestInvDepth] = $dat;
170 if (isset($pageTree->tree[$cc - 1])) {
171 $prev_dat = $pageTree->tree[$cc - 1];
172 // If current page, subpage?
173 if ($prev_dat['row']['uid'] == $id) {
174 // 1) It must be allowed to create a new page and 2) If there are subpages there is no need to render a subpage icon here - it'll be done over the subpages...
175 if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($id) && !($prev_dat['invertedDepth'] > $pageTree->tree[$cc]['invertedDepth'])) {
176 end($lines);
177 $lines[] = '<li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($id, $id, 1)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li>';
178 }
179 }
180 // If going down
181 if ($prev_dat['invertedDepth'] > $pageTree->tree[$cc]['invertedDepth']) {
182 $prevPid = $pageTree->tree[$cc]['row']['pid'];
183 } elseif ($prev_dat['invertedDepth'] < $pageTree->tree[$cc]['invertedDepth']) {
184 // If going up
185 // First of all the previous level should have an icon:
186 if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($prev_dat['row']['pid'])) {
187 $prevPid = -$prev_dat['row']['uid'];
188 end($lines);
189 $lines[] = '<li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $prev_dat['row']['pid'], 2)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li>';
190 }
191 // Then set the current prevPid
192 $prevPid = -$prev_dat['row']['pid'];
193 } else {
194 // In on the same level
195 $prevPid = -$prev_dat['row']['uid'];
196 }
197 } else {
198 // First in the tree
199 $prevPid = $dat['row']['pid'];
200 }
201 // print arrow on the same level
202 if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($dat['row']['pid'])) {
203 if (!empty($prev_dat) && $prev_dat['invertedDepth'] < $dat['invertedDepth']) {
204 $lines[] = '</ul>';
205 }
206 $lines[] = '<span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $dat['row']['pid'], 3)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span>';
207 }
208 // The line with the icon and title:
209 $toolTip = BackendUtility::getRecordToolTip($dat['row'], 'pages');
210 $icon = '<span ' . $toolTip . '>' . $this->iconFactory->getIconForRecord('pages', $dat['row'], Icon::SIZE_SMALL)->render() . '</span>';
211
212 $lines[] = '<span class="text-nowrap">' . $icon . $this->linkPageTitle($this->boldTitle(htmlspecialchars(GeneralUtility::fixed_lgd_cs($dat['row']['title'], $this->getBackendUser()->uc['titleLen'])), $dat, $id), $dat['row']) . '</span>';
213 }
214 // If the current page was the last in the tree:
215 $prev_dat = end($pageTree->tree);
216 if ($prev_dat['row']['uid'] == $id) {
217 if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($id)) {
218 $lines[] = '<ul class="list-tree" style="margin-left: 25px"><li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($id, $id, 4)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li></ul>';
219 }
220 }
221 for ($a = $latestInvDepth; $a <= $this->depth; $a++) {
222 $dat = $saveLatestUid[$a];
223 $prevPid = -$dat['row']['uid'];
224 if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($dat['row']['pid'])) {
225 if ($latestInvDepth < $dat['invertedDepth']) {
226 $lines[] = '</ul>';
227 }
228 $lines[] = '<span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $dat['row']['pid'], 5)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span>';
229 }
230 }
231
232 $code = '<ul class="list-tree">';
233
234 foreach ($lines as $line) {
235 if ((substr($line, 0, 3) === '<ul') || (substr($line, 0, 4) === '</ul')) {
236 $code .= $line;
237 } else {
238 $code .= '<li>' . $line . '</li>';
239 }
240 }
241
242 $code .= '</ul>';
243 return $code;
244 }
245
246 /**
247 * Wrap $t_code in bold IF the $dat uid matches $id
248 *
249 * @param string $t_code Title string
250 * @param array $dat Infomation array with record array inside.
251 * @param int $id The current id.
252 * @return string The title string.
253 */
254 public function boldTitle($t_code, $dat, $id)
255 {
256 if ($dat['row']['uid'] == $id) {
257 $t_code = '<strong>' . $t_code . '</strong>';
258 }
259 return $t_code;
260 }
261
262 /**
263 * Creates the onclick event for the insert-icons.
264 *
265 * TSconfig mod.web_list.newPageWiz.overrideWithExtension may contain an extension which provides a module
266 * to be used instead of the normal create new page wizard.
267 *
268 * @param int $pid The pid.
269 * @param int $newPagePID New page id.
270 * @return string Onclick attribute content
271 */
272 public function onClickEvent($pid, $newPagePID)
273 {
274 $TSconfigProp = $this->getModConfig($newPagePID);
275 if ($TSconfigProp['overrideWithExtension']) {
276 if (ExtensionManagementUtility::isLoaded($TSconfigProp['overrideWithExtension'])) {
277 $onclick = 'window.location.href=' . GeneralUtility::quoteJSvalue(PathUtility::getAbsoluteWebPath(ExtensionManagementUtility::extPath($TSconfigProp['overrideWithExtension'])) . 'mod1/index.php?cmd=crPage&positionPid=' . $pid) . ';';
278 return $onclick;
279 }
280 }
281 $params = '&edit[pages][' . $pid . ']=new&returnNewPageId=1';
282 return BackendUtility::editOnClick($params, '', $this->R_URI);
283 }
284
285 /**
286 * Get label, htmlspecialchars()'ed
287 *
288 * @return string The localized label for "insert new page here
289 */
290 public function insertlabel()
291 {
292 return htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewPageHere));
293 }
294
295 /**
296 * Wrapping page title.
297 *
298 * @param string $str Page title.
299 * @param array $rec Page record (?)
300 * @return string Wrapped title.
301 */
302 public function linkPageTitle($str, $rec)
303 {
304 return $str;
305 }
306
307 /**
308 * Checks if the user has permission to created pages inside of the $pid page.
309 * Uses caching so only one regular lookup is made - hence you can call the function multiple times without worrying about performance.
310 *
311 * @param int $pid Page id for which to test.
312 * @return bool
313 */
314 public function checkNewPageInPid($pid)
315 {
316 if (!isset($this->checkNewPageCache[$pid])) {
317 $pidInfo = BackendUtility::getRecord('pages', $pid);
318 $this->checkNewPageCache[$pid] = $this->getBackendUser()->isAdmin() || $this->getBackendUser()->doesUserHaveAccess($pidInfo, 8);
319 }
320 return $this->checkNewPageCache[$pid];
321 }
322
323 /**
324 * Returns module configuration for a pid.
325 *
326 * @param int $pid Page id for which to get the module configuration.
327 * @return array The properties of teh module configuration for the page id.
328 * @see onClickEvent()
329 */
330 public function getModConfig($pid)
331 {
332 if (!isset($this->getModConfigCache[$pid])) {
333 // Acquiring TSconfig for this PID:
334 $this->getModConfigCache[$pid] = BackendUtility::getModTSconfig($pid, $this->modConfigStr);
335 }
336 return $this->getModConfigCache[$pid]['properties'];
337 }
338
339 /*************************************
340 *
341 * Content element positioning:
342 *
343 **************************************/
344 /**
345 * Creates HTML for inserting/moving content elements.
346 *
347 * @param int $pid page id onto which to insert content element.
348 * @param int $moveUid Move-uid (tt_content element uid?)
349 * @param string $colPosList List of columns to show
350 * @param bool $showHidden If not set, then hidden/starttime/endtime records are filtered out.
351 * @param string $R_URI Request URI
352 * @return string HTML
353 */
354 public function printContentElementColumns($pid, $moveUid, $colPosList, $showHidden, $R_URI)
355 {
356 $this->R_URI = $R_URI;
357 $this->moveUid = $moveUid;
358 $colPosArray = GeneralUtility::trimExplode(',', $colPosList, true);
359 $lines = [];
360 foreach ($colPosArray as $kk => $vv) {
361 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
362 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
363 if ($showHidden) {
364 $queryBuilder->getRestrictions()
365 ->removeByType(HiddenRestriction::class)
366 ->removeByType(StartTimeRestriction::class)
367 ->removeByType(EndTimeRestriction::class);
368 }
369 $queryBuilder
370 ->select('*')
371 ->from('tt_content')
372 ->where(
373 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
374 $queryBuilder->expr()->eq('colPos', $queryBuilder->createNamedParameter($vv, \PDO::PARAM_INT))
375 )
376 ->orderBy('sorting');
377
378 if ((string)$this->cur_sys_language !== '') {
379 $queryBuilder->andWhere(
380 $queryBuilder->expr()->eq(
381 'sys_language_uid',
382 $queryBuilder->createNamedParameter($this->cur_sys_language, \PDO::PARAM_INT)
383 )
384 );
385 }
386
387 $res = $queryBuilder->execute();
388 $lines[$vv] = [];
389 $lines[$vv][] = $this->insertPositionIcon('', $vv, $kk, $moveUid, $pid);
390
391 while ($row = $res->fetch()) {
392 BackendUtility::workspaceOL('tt_content', $row);
393 if (is_array($row)) {
394 $lines[$vv][] = $this->wrapRecordHeader($this->getRecordHeader($row), $row);
395 $lines[$vv][] = $this->insertPositionIcon($row, $vv, $kk, $moveUid, $pid);
396 }
397 }
398 }
399 return $this->printRecordMap($lines, $colPosArray, $pid);
400 }
401
402 /**
403 * Creates the table with the content columns
404 *
405 * @param array $lines Array with arrays of lines for each column
406 * @param array $colPosArray Column position array
407 * @param int $pid The id of the page
408 * @return string HTML
409 */
410 public function printRecordMap($lines, $colPosArray, $pid = 0)
411 {
412 $count = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange(count($colPosArray), 1);
413 $backendLayout = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getSelectedBackendLayout', $pid, $this);
414 if (isset($backendLayout['__config']['backend_layout.'])) {
415 $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
416 $table = '<div class="table-fit"><table class="table table-condensed table-bordered table-vertical-top">';
417 $colCount = (int)$backendLayout['__config']['backend_layout.']['colCount'];
418 $rowCount = (int)$backendLayout['__config']['backend_layout.']['rowCount'];
419 $table .= '<colgroup>';
420 for ($i = 0; $i < $colCount; $i++) {
421 $table .= '<col style="width:' . 100 / $colCount . '%"></col>';
422 }
423 $table .= '</colgroup>';
424 $table .= '<tbody>';
425 $tcaItems = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $pid, $this);
426 // Cycle through rows
427 for ($row = 1; $row <= $rowCount; $row++) {
428 $rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.'];
429 if (!isset($rowConfig)) {
430 continue;
431 }
432 $table .= '<tr>';
433 for ($col = 1; $col <= $colCount; $col++) {
434 $columnConfig = $rowConfig['columns.'][$col . '.'];
435 if (!isset($columnConfig)) {
436 continue;
437 }
438 // Which tt_content colPos should be displayed inside this cell
439 $columnKey = (int)$columnConfig['colPos'];
440 $head = '';
441 foreach ($tcaItems as $item) {
442 if ($item[1] == $columnKey) {
443 $head = htmlspecialchars($this->getLanguageService()->sL($item[0]));
444 }
445 }
446 // Render the grid cell
447 $table .= '<td'
448 . (isset($columnConfig['colspan']) ? ' colspan="' . $columnConfig['colspan'] . '"' : '')
449 . (isset($columnConfig['rowspan']) ? ' rowspan="' . $columnConfig['rowspan'] . '"' : '')
450 . ' class="col-nowrap col-min'
451 . (!isset($columnConfig['colPos']) ? ' warning' : '')
452 . (isset($columnConfig['colPos']) && !$head ? ' danger' : '') . '">';
453 // Render header
454 $table .= '<p>';
455 if (isset($columnConfig['colPos']) && $head) {
456 $table .= '<strong>' . $this->wrapColumnHeader($head, '', '') . '</strong>';
457 } elseif ($columnConfig['colPos']) {
458 $table .= '<em>' . $this->wrapColumnHeader($this->getLanguageService()->getLL('noAccess'), '', '') . '</em>';
459 } else {
460 $table .= '<em>' . $this->wrapColumnHeader(($columnConfig['name']?: '') . ' (' . $this->getLanguageService()->getLL('notAssigned') . ')', '', '') . '</em>';
461 }
462 $table .= '</p>';
463 // Render lines
464 if (isset($columnConfig['colPos']) && $head && !empty($lines[$columnKey])) {
465 $table .= '<ul class="list-unstyled">';
466 foreach ($lines[$columnKey] as $line) {
467 $table .= '<li>' . $line . '</li>';
468 }
469 $table .= '</ul>';
470 }
471 $table .= '</td>';
472 }
473 $table .= '</tr>';
474 }
475 $table .= '</tbody>';
476 $table .= '</table></div>';
477 } else {
478 // Traverse the columns here:
479 $row = '';
480 foreach ($colPosArray as $kk => $vv) {
481 $row .= '<td class="col-nowrap col-min" width="' . round(100 / $count) . '%">';
482 $row .= '<p><strong>' . $this->wrapColumnHeader(htmlspecialchars($this->getLanguageService()->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $vv))), $vv) . '</strong></p>';
483 if (!empty($lines[$vv])) {
484 $row .= '<ul class="list-unstyled">';
485 foreach ($lines[$vv] as $line) {
486 $row .= '<li>' . $line . '</li>';
487 }
488 $row .= '</ul>';
489 }
490 $row .= '</td>';
491 }
492 $table = '
493
494 <!--
495 Map of records in columns:
496 -->
497 <div class="table-fit">
498 <table class="table table-condensed table-bordered table-vertical-top">
499 <tr>' . $row . '</tr>
500 </table>
501 </div>
502
503 ';
504 }
505 return $table;
506 }
507
508 /**
509 * Wrapping the column header
510 *
511 * @param string $str Header value
512 * @param string $vv Column info.
513 * @return string
514 * @see printRecordMap()
515 */
516 public function wrapColumnHeader($str, $vv)
517 {
518 return $str;
519 }
520
521 /**
522 * Creates a linked position icon.
523 *
524 * @param mixed $row Element row. If this is an array the link will cause an insert after this content element, otherwise
525 * the link will insert at the first position in the column
526 * @param string $vv Column position value.
527 * @param int $kk Column key.
528 * @param int $moveUid Move uid
529 * @param int $pid PID value.
530 * @return string
531 */
532 public function insertPositionIcon($row, $vv, $kk, $moveUid, $pid)
533 {
534 if (is_array($row) && !empty($row['uid'])) {
535 // Use record uid for the hash when inserting after this content element
536 $uid = $row['uid'];
537 } else {
538 // No uid means insert at first position in the column
539 $uid = '';
540 }
541 $cc = hexdec(substr(md5($uid . '-' . $vv . '-' . $kk), 0, 4));
542 return '<a href="#" onclick="' . htmlspecialchars($this->onClickInsertRecord($row, $vv, $moveUid, $pid, $this->cur_sys_language)) . '">' . '<i class="t3-icon fa fa-long-arrow-left" name="mImgEnd' . $cc . '" title="' . htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewRecordHere)) . '"></i></a>';
543 }
544
545 /**
546 * Create on-click event value.
547 *
548 * @param mixed $row The record. If this is not an array with the record data the insert will be for the first position
549 * in the column
550 * @param string $vv Column position value.
551 * @param int $moveUid Move uid
552 * @param int $pid PID value.
553 * @param int $sys_lang System language (not used currently)
554 * @return string
555 */
556 public function onClickInsertRecord($row, $vv, $moveUid, $pid, $sys_lang = 0)
557 {
558 $table = 'tt_content';
559 if (is_array($row)) {
560 $location = BackendUtility::getModuleUrl('tce_db') . '&cmd[' . $table . '][' . $moveUid . '][' . $this->moveOrCopy . ']=-' . $row['uid'] . '&prErr=1&uPT=1&vC=' . $this->getBackendUser()->veriCode();
561 } else {
562 $location = BackendUtility::getModuleUrl('tce_db') . '&cmd[' . $table . '][' . $moveUid . '][' . $this->moveOrCopy . ']=' . $pid . '&data[' . $table . '][' . $moveUid . '][colPos]=' . $vv . '&prErr=1&vC=' . $this->getBackendUser()->veriCode();
563 }
564 $location .= '&redirect=' . rawurlencode($this->R_URI);
565 // returns to prev. page
566 return 'window.location.href=' . GeneralUtility::quoteJSvalue($location) . ';return false;';
567 }
568
569 /**
570 * Wrapping the record header (from getRecordHeader())
571 *
572 * @param string $str HTML content
573 * @param string $row Record array.
574 * @return string HTML content
575 */
576 public function wrapRecordHeader($str, $row)
577 {
578 return $str;
579 }
580
581 /**
582 * Create record header (includes teh record icon, record title etc.)
583 *
584 * @param array $row Record row.
585 * @return string HTML
586 */
587 public function getRecordHeader($row)
588 {
589 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
590 $toolTip = BackendUtility::getRecordToolTip($row, 'tt_content');
591 $line = '<span ' . $toolTip . ' title="' . BackendUtility::getRecordIconAltText($row, 'tt_content') . '">' . $iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render() . '</span>';
592 $line .= BackendUtility::getRecordTitle('tt_content', $row, true);
593 return $this->wrapRecordTitle($line, $row);
594 }
595
596 /**
597 * Wrapping the title of the record.
598 *
599 * @param string $str The title value.
600 * @param array $row The record row.
601 * @return string Wrapped title string.
602 */
603 public function wrapRecordTitle($str, $row)
604 {
605 return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(['uid' => (int)$row['uid'], 'moveUid' => ''])) . '">' . $str . '</a>';
606 }
607
608 /**
609 * Returns the BackendUser
610 *
611 * @return BackendUserAuthentication
612 */
613 protected function getBackendUser()
614 {
615 return $GLOBALS['BE_USER'];
616 }
617
618 /**
619 * Returns the LanguageService
620 *
621 * @return LanguageService
622 */
623 protected function getLanguageService()
624 {
625 return $GLOBALS['LANG'];
626 }
627 }