‪TYPO3CMS  ‪main
StandardContentPreviewRenderer.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Log\LoggerAwareInterface;
21 use Psr\Log\LoggerAwareTrait;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
28 use TYPO3\CMS\Core\Imaging\IconSize;
34 
48 class ‪StandardContentPreviewRenderer implements ‪PreviewRendererInterface, LoggerAwareInterface
49 {
50  use LoggerAwareTrait;
51 
57  private const ‪MENU_CONTENT_TYPES = [
58  'menu_abstract',
59  'menu_categorized_content',
60  'menu_categorized_pages',
61  'menu_pages',
62  'menu_recently_updated',
63  'menu_related_pages',
64  'menu_section',
65  'menu_section_pages',
66  'menu_sitemap',
67  'menu_sitemap_pages',
68  'menu_subpages',
69  ];
70 
71  public function ‪renderPageModulePreviewHeader(‪GridColumnItem $item): string
72  {
73  ‪$record = $item->‪getRecord();
74  $itemLabels = $item->‪getContext()->getItemLabels();
75 
76  $outHeader = '';
77  $outHeader .= ‪$record['header_layout'] == 100
78  ? '<div class="element-preview-header-status">' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:header_layout.I.6')) . '</div>'
79  : '';
80  $outHeader .= ‪$record['date']
81  ? '<div class="element-preview-header-date">' . htmlspecialchars($itemLabels['date'] . ' ' . BackendUtility::date(‪$record['date'])) . ' </div>'
82  : '';
83  $outHeader .= ‪$record['header']
84  ? '<div class="element-preview-header-header">' . $this->‪linkEditContent($this->‪renderText(‪$record['header']), ‪$record) . '</div>'
85  : '';
86  $outHeader .= ‪$record['subheader']
87  ? '<div class="element-preview-header-subheader">' . $this->‪linkEditContent($this->‪renderText(‪$record['subheader']), ‪$record) . '</div>'
88  : '';
89 
90  return $outHeader;
91  }
92 
94  {
95  $out = '';
96  ‪$record = $item->‪getRecord();
97 
98  $contentTypeLabels = $item->‪getContext()->getContentTypeLabels();
99  $languageService = $this->‪getLanguageService();
100 
101  // Check if a Fluid-based preview template was defined for this CType
102  // and render it via Fluid. Possible option:
103  // mod.web_layout.tt_content.preview.media = EXT:site_mysite/Resources/Private/Templates/Preview/Media.html
104  $infoArr = [];
105  $this->‪getProcessedValue($item, 'header_position,header_layout,header_link', $infoArr);
106  $tsConfig = BackendUtility::getPagesTSconfig(‪$record['pid'])['mod.']['web_layout.']['tt_content.']['preview.'] ?? [];
107  if (!empty($tsConfig[‪$record['CType']]) || !empty($tsConfig[‪$record['CType'] . '.'])) {
108  $fluidPreview = $this->‪renderContentElementPreviewFromFluidTemplate($record);
109  if ($fluidPreview !== null) {
110  return $fluidPreview;
111  }
112  }
113 
114  // Draw preview of the item depending on its CType
115  switch (‪$record['CType']) {
116  case 'header':
117  break;
118  case 'uploads':
119  if (‪$record['media']) {
120  $out .= $this->‪linkEditContent($this->‪getThumbCodeUnlinked($record, 'tt_content', 'media'), ‪$record);
121  }
122  break;
123  case 'shortcut':
124  if (!empty(‪$record['records'])) {
125  $shortcutContent = '';
126  $recordList = explode(',', ‪$record['records']);
127  foreach ($recordList as $recordIdentifier) {
128  $split = BackendUtility::splitTable_Uid($recordIdentifier);
129  $tableName = empty($split[0]) ? 'tt_content' : $split[0];
130  $shortcutRecord = BackendUtility::getRecord($tableName, $split[1]);
131  if (is_array($shortcutRecord)) {
132  $shortcutRecord = $this->‪translateShortcutRecord($record, $shortcutRecord, $tableName, (int)$split[1]);
133  $icon = $this->‪getIconFactory()->getIconForRecord($tableName, $shortcutRecord, IconSize::SMALL)->render();
134  $icon = BackendUtility::wrapClickMenuOnIcon(
135  $icon,
136  $tableName,
137  $shortcutRecord['uid'],
138  '1'
139  );
140  $shortcutContent .= '<li class="list-group-item">' . $icon . ' ' . htmlspecialchars(BackendUtility::getRecordTitle($tableName, $shortcutRecord)) . '</li>';
141  }
142  }
143  $out .= $shortcutContent ? '<ul class="list-group">' . $shortcutContent . '</ul>' : '';
144  }
145  break;
146  case 'list':
147  if (!empty(‪$record['list_type'])) {
148  $label = BackendUtility::getLabelFromItemListMerged((int)‪$record['pid'], 'tt_content', 'list_type', ‪$record['list_type'], ‪$record);
149  if (!empty($label)) {
150  $out .= $this->‪linkEditContent('<strong>' . htmlspecialchars($languageService->sL($label)) . '</strong>', ‪$record);
151  } else {
152  $message = sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue'), ‪$record['list_type']);
153  $out .= '<div class="alert alert-danger">' . htmlspecialchars($message) . '</div>';
154  }
155  } else {
156  $out .= '<div class="alert alert-warning">' . htmlspecialchars($languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:noPluginSelected')) . '</div>';
157  }
158  break;
159  default:
160  $contentTypeLabel = (string)($contentTypeLabels[‪$record['CType']] ?? '');
161  if ($contentTypeLabel === '') {
162  $message = sprintf(
163  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue'),
164  ‪$record['CType']
165  );
166  $out .= '<span class="badge badge-warning">' . htmlspecialchars($message) . '</span>';
167  break;
168  }
169  // Handle menu content types
170  if (in_array(‪$record['CType'], self::MENU_CONTENT_TYPES, true)) {
171  if (‪$record['CType'] !== 'menu_sitemap' && ((‪$record['pages'] ?? false) || (‪$record['selected_categories'] ?? false))) {
172  // Show pages/categories if menu type is not "Sitemap"
173  $out .= $this->‪linkEditContent($this->‪generateListForMenuContentTypes($record), ‪$record);
174  }
175  break;
176  }
177  if (‪$record['bodytext']) {
178  $out .= $this->‪linkEditContent($this->‪renderText($record['bodytext']), ‪$record);
179  }
180  if (‪$record['image']) {
181  $out .= $this->‪linkEditContent($this->‪getThumbCodeUnlinked($record, 'tt_content', 'image'), ‪$record);
182  }
183  }
184 
185  return $out;
186  }
187 
192  {
193  $info = [];
194  ‪$record = $item->‪getRecord();
195  $this->‪getProcessedValue($item, 'starttime,endtime,fe_group,space_before_class,space_after_class', $info);
196 
197  if (!empty(‪$GLOBALS['TCA']['tt_content']['ctrl']['descriptionColumn']) && !empty(‪$record[‪$GLOBALS['TCA']['tt_content']['ctrl']['descriptionColumn']])) {
198  $info[] = htmlspecialchars(‪$record[‪$GLOBALS['TCA']['tt_content']['ctrl']['descriptionColumn']]);
199  }
200 
201  if (!empty($info)) {
202  return implode('<br>', $info);
203  }
204  return '';
205  }
206 
207  public function ‪wrapPageModulePreview(string $previewHeader, string $previewContent, ‪GridColumnItem $item): string
208  {
209  $previewHeader = $previewHeader ? '<div class="element-preview-header">' . $previewHeader . '</div>' : '';
210  $previewContent = $previewContent ? '<div class="element-preview-content">' . $previewContent . '</div>' : '';
211  $preview = $previewHeader || $previewContent ? '<div class="element-preview">' . $previewHeader . $previewContent . '</div>' : '';
212 
213  return $preview;
214  }
215 
216  protected function ‪translateShortcutRecord(array $targetRecord, array $shortcutRecord, string $tableName, int ‪$uid): array
217  {
218  $targetLanguage = (int)($targetRecord['sys_language_uid'] ?? 0);
219  if ($targetLanguage === 0 || !BackendUtility::isTableLocalizable($tableName)) {
220  return $shortcutRecord;
221  }
222 
223  $languageField = ‪$GLOBALS['TCA'][$tableName]['ctrl']['languageField'];
224  $shortcutLanguage = (int)($shortcutRecord[$languageField] ?? 0);
225  if ($targetLanguage === $shortcutLanguage) {
226  return $shortcutRecord;
227  }
228 
229  // record is localized - fetch the shortcut record translation, if available
230  $shortcutRecordLocalization = BackendUtility::getRecordLocalization($tableName, ‪$uid, $targetLanguage);
231  if (is_array($shortcutRecordLocalization) && !empty($shortcutRecordLocalization)) {
232  $shortcutRecord = $shortcutRecordLocalization[0];
233  }
234 
235  return $shortcutRecord;
236  }
237 
238  protected function ‪getProcessedValue(‪GridColumnItem $item, string $fieldList, array &$info): void
239  {
240  $itemLabels = $item->‪getContext()->getItemLabels();
241  ‪$record = $item->‪getRecord();
242  $fieldArr = explode(',', $fieldList);
243  foreach ($fieldArr as $field) {
244  if (‪$record[$field]) {
245  $fieldValue = BackendUtility::getProcessedValue('tt_content', $field, ‪$record[$field], 0, false, false, ‪$record['uid'] ?? 0, true, ‪$record['pid'] ?? 0) ?? '';
246  $info[] = '<strong>' . htmlspecialchars((string)($itemLabels[$field] ?? '')) . '</strong> ' . htmlspecialchars($fieldValue);
247  }
248  }
249  }
250 
251  protected function ‪renderContentElementPreviewFromFluidTemplate(array $row): ?string
252  {
253  $tsConfig = BackendUtility::getPagesTSconfig($row['pid'])['mod.']['web_layout.']['tt_content.']['preview.'] ?? [];
254  $fluidTemplateFile = '';
255 
256  if ($row['CType'] === 'list' && !empty($row['list_type'])
257  && !empty($tsConfig['list.'][$row['list_type']])
258  ) {
259  $fluidTemplateFile = $tsConfig['list.'][$row['list_type']];
260  } elseif (!empty($tsConfig[$row['CType']])) {
261  $fluidTemplateFile = $tsConfig[$row['CType']];
262  }
263 
264  if ($fluidTemplateFile) {
265  $fluidTemplateFile = GeneralUtility::getFileAbsFileName($fluidTemplateFile);
266  if ($fluidTemplateFile) {
267  try {
268  $view = GeneralUtility::makeInstance(StandaloneView::class);
269  $view->setTemplatePathAndFilename($fluidTemplateFile);
270  $view->assignMultiple($row);
271  if (!empty($row['pi_flexform'])) {
272  $flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
273  $view->assign('pi_flexform_transformed', $flexFormService->convertFlexFormContentToArray($row['pi_flexform']));
274  }
275  return $view->render();
276  } catch (\‪Exception $e) {
277  $this->logger->warning('The backend preview for content element {uid} can not be rendered using the Fluid template file "{file}"', [
278  'uid' => $row['uid'],
279  'file' => $fluidTemplateFile,
280  'exception' => $e,
281  ]);
282 
283  if ($this->‪getBackendUser()->shallDisplayDebugInformation()) {
284  $view = GeneralUtility::makeInstance(StandaloneView::class);
285  $view->assign('error', [
286  'message' => str_replace(‪Environment::getProjectPath(), '', $e->getMessage()),
287  'title' => 'Error while rendering FluidTemplate preview using ' . str_replace(‪Environment::getProjectPath(), '', $fluidTemplateFile),
288  ]);
289  $view->setTemplateSource('<f:be.infobox title="{error.title}" state="2">{error.message}</f:be.infobox>');
290  return $view->render();
291  }
292  }
293  }
294  }
295  return null;
296  }
297 
306  protected function ‪getThumbCodeUnlinked($row, $table, $field): string
307  {
308  return BackendUtility::thumbCode(row: $row, table: $table, field: $field, linkInfoPopup: false);
309  }
310 
317  protected function ‪renderText(string $input): string
318  {
319  $input = strip_tags($input);
320  $input = ‪GeneralUtility::fixed_lgd_cs($input, 1500);
321  return nl2br(htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8', false));
322  }
323 
329  protected function ‪generateListForMenuContentTypes(array ‪$record): string
330  {
331  $table = 'pages';
332  $field = 'pages';
333  // get categories instead of pages
334  if (str_contains(‪$record['CType'], 'menu_categorized')) {
335  $table = 'sys_category';
336  $field = 'selected_categories';
337  }
338  if (trim(‪$record[$field] ?? '') === '') {
339  return '';
340  }
341  $content = '';
342  $uidList = explode(',', ‪$record[$field]);
343  foreach ($uidList as ‪$uid) {
344  ‪$uid = (int)‪$uid;
345  $pageRecord = BackendUtility::getRecord($table, ‪$uid, 'title');
346  if ($pageRecord) {
347  $content .= '<li class="list-group-item">' . htmlspecialchars($pageRecord['title']) . ' <span class="text-body-secondary">[' . ‪$uid . ']</span></li>';
348  }
349  }
350  return $content ? '<ul class="list-group">' . $content . '</ul>' : '';
351  }
352 
361  protected function ‪linkEditContent(string $linkText, $row): string
362  {
363  if (empty($linkText)) {
364  return $linkText;
365  }
366 
367  $backendUser = $this->‪getBackendUser();
368  if ($backendUser->check('tables_modify', 'tt_content')
369  && $backendUser->recordEditAccessInternals('tt_content', $row)
370  && (new ‪Permission($backendUser->calcPerms(BackendUtility::getRecord('pages', $row['pid']) ?? [])))->editContentPermissionIsGranted()
371  ) {
372  $urlParameters = [
373  'edit' => [
374  'tt_content' => [
375  $row['uid'] => 'edit',
376  ],
377  ],
378  'returnUrl' => ‪$GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri() . '#element-tt_content-' . $row['uid'],
379  ];
380  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
381  ‪$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
382  return '<a href="' . htmlspecialchars(‪$url) . '" title="' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:edit')) . '">' . $linkText . '</a>';
383  }
384  return $linkText;
385  }
386 
388  {
389  return ‪$GLOBALS['BE_USER'];
390  }
391 
393  {
394  return ‪$GLOBALS['LANG'];
395  }
396 
397  protected function ‪getIconFactory(): ‪IconFactory
398  {
399  return GeneralUtility::makeInstance(IconFactory::class);
400  }
401 }
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\getIconFactory
‪getIconFactory()
Definition: StandardContentPreviewRenderer.php:397
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\renderPageModulePreviewHeader
‪renderPageModulePreviewHeader(GridColumnItem $item)
Definition: StandardContentPreviewRenderer.php:71
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer
Definition: StandardContentPreviewRenderer.php:49
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\getProcessedValue
‪getProcessedValue(GridColumnItem $item, string $fieldList, array &$info)
Definition: StandardContentPreviewRenderer.php:238
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\translateShortcutRecord
‪translateShortcutRecord(array $targetRecord, array $shortcutRecord, string $tableName, int $uid)
Definition: StandardContentPreviewRenderer.php:216
‪TYPO3\CMS\Backend\View\BackendLayout\Grid\AbstractGridObject\getContext
‪getContext()
Definition: AbstractGridObject.php:54
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\renderContentElementPreviewFromFluidTemplate
‪renderContentElementPreviewFromFluidTemplate(array $row)
Definition: StandardContentPreviewRenderer.php:251
‪TYPO3\CMS\Backend\Preview\PreviewRendererInterface
Definition: PreviewRendererInterface.php:40
‪TYPO3\CMS\Backend\Exception
Definition: Exception.php:23
‪TYPO3\CMS\Backend\Preview
Definition: PreviewRendererInterface.php:18
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\renderPageModulePreviewContent
‪renderPageModulePreviewContent(GridColumnItem $item)
Definition: StandardContentPreviewRenderer.php:93
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\wrapPageModulePreview
‪wrapPageModulePreview(string $previewHeader, string $previewContent, GridColumnItem $item)
Definition: StandardContentPreviewRenderer.php:207
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\renderText
‪string renderText(string $input)
Definition: StandardContentPreviewRenderer.php:317
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Core\Service\FlexFormService
Definition: FlexFormService.php:25
‪TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem\getRecord
‪getRecord()
Definition: GridColumnItem.php:203
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\getLanguageService
‪getLanguageService()
Definition: StandardContentPreviewRenderer.php:392
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\getThumbCodeUnlinked
‪string getThumbCodeUnlinked($row, $table, $field)
Definition: StandardContentPreviewRenderer.php:306
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\renderPageModulePreviewFooter
‪renderPageModulePreviewFooter(GridColumnItem $item)
Definition: StandardContentPreviewRenderer.php:191
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:61
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:30
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\getBackendUser
‪getBackendUser()
Definition: StandardContentPreviewRenderer.php:387
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\generateListForMenuContentTypes
‪generateListForMenuContentTypes(array $record)
Definition: StandardContentPreviewRenderer.php:329
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\linkEditContent
‪string linkEditContent(string $linkText, $row)
Definition: StandardContentPreviewRenderer.php:361
‪TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem
Definition: GridColumnItem.php:45
‪TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer\MENU_CONTENT_TYPES
‪const MENU_CONTENT_TYPES
Definition: StandardContentPreviewRenderer.php:57