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