TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Backend\Preview;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\DataHandling\TableColumnType;
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
use TYPO3\CMS\Core\Html\SanitizerBuilderFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Resource\ProcessedFile;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Use this to render certain fields (label + values) as kind of shortcut or helper
|
||||
* methods when implementing your own Preview Renderer.
|
||||
*
|
||||
* If you need your custom rendering, build your own renderer for your own PreviewRenderer
|
||||
* class that you can then inject.
|
||||
*
|
||||
* The result is always HTML, and it's always HSCed -> ready to be rendered.
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class RecordFieldPreviewProcessor
|
||||
{
|
||||
public function __construct(
|
||||
private TcaSchemaFactory $schemaFactory,
|
||||
private UriBuilder $uriBuilder,
|
||||
private IconFactory $iconFactory,
|
||||
private SanitizerBuilderFactory $sanitizerBuilderFactory,
|
||||
#[Autowire(service: 'cache.runtime')]
|
||||
private FrontendInterface $runtimeCache,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Prepare the value of a field but prepend the label before.
|
||||
*/
|
||||
public function prepareFieldWithLabel(RecordInterface $record, string $fieldName): ?string
|
||||
{
|
||||
if ($record->has($fieldName)) {
|
||||
$table = $record->getMainType();
|
||||
$value = $record->get($fieldName);
|
||||
if ($value !== '' && $value !== null) {
|
||||
$itemLabels = $this->getItemLabels($record);
|
||||
$fieldValue = BackendUtility::getProcessedValue($table, $fieldName, $value, 0, false, false, $record->getUid(), true, $record->getPid(), $record->getRawRecord()->toArray()) ?? '';
|
||||
if ($fieldValue !== '') {
|
||||
return htmlspecialchars((string)($itemLabels[$fieldName] ?? '')) . ': ' . htmlspecialchars((string)$fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the value of a field if it exists and it is not empty.
|
||||
*/
|
||||
public function prepareField(RecordInterface $record, string $fieldName): ?string
|
||||
{
|
||||
if ($record->has($fieldName)) {
|
||||
$table = $record->getMainType();
|
||||
$value = $record->get($fieldName);
|
||||
if ($value !== '' && $value !== null) {
|
||||
$fieldValue = BackendUtility::getProcessedValue($table, $fieldName, $value, 0, false, false, $record->getUid(), true, $record->getPid(), $record->getRawRecord()->toArray()) ?? '';
|
||||
if ($fieldValue !== '') {
|
||||
return htmlspecialchars((string)$fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing of larger amounts of text (usually from RTE/bodytext fields) with word wrapping, etc.
|
||||
*/
|
||||
public function prepareText(RecordInterface $record, string $fieldName, int $maxLength = 1500): ?string
|
||||
{
|
||||
if ($record->has($fieldName)) {
|
||||
$input = $record->get($fieldName);
|
||||
$schemaForType = $this->schemaFactory->get($record->getFullType());
|
||||
if (is_string($input) && $input !== '' && $schemaForType->hasField($fieldName)) {
|
||||
$isSimpleText = $schemaForType->getField($fieldName)->isType(TableColumnType::INPUT);
|
||||
if (!$isSimpleText) {
|
||||
$input = strip_tags($input);
|
||||
}
|
||||
$input = GeneralUtility::fixed_lgd_cs($input, $maxLength);
|
||||
return nl2br(htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8', false));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function preparePlainHtml(RecordInterface $record, string $fieldName, int $maxLines = 100): ?string
|
||||
{
|
||||
if ($record->has($fieldName)) {
|
||||
$html = GeneralUtility::trimExplode(LF, (string)$record->get($fieldName), true);
|
||||
if ($html !== []) {
|
||||
$html = array_slice($html, 0, $maxLines);
|
||||
return str_replace(LF, '<br />', htmlspecialchars(implode(LF, $html)));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function preparePreviewableHtml(RecordInterface $record, string $fieldName): ?string
|
||||
{
|
||||
if ($record->has($fieldName)) {
|
||||
$content = $record->get($fieldName);
|
||||
if (!is_string($content)) {
|
||||
return null;
|
||||
}
|
||||
$builder = $this->sanitizerBuilderFactory->build('preview');
|
||||
return $builder->build()->sanitize($content);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render thumbnails for a file collection or files.
|
||||
*/
|
||||
public function prepareFiles(iterable|FileReference $fileReferences): ?string
|
||||
{
|
||||
$thumbData = [];
|
||||
$fileReferences = $fileReferences instanceof FileReference ? [$fileReferences] : $fileReferences;
|
||||
foreach ($fileReferences as $fileReferenceObject) {
|
||||
// Do not show previews of hidden references
|
||||
if ($fileReferenceObject->getProperty('hidden')) {
|
||||
continue;
|
||||
}
|
||||
$fileObject = $fileReferenceObject->getOriginalFile();
|
||||
if ($fileObject->isMissing()) {
|
||||
$thumbData[] = $this->iconFactory
|
||||
->getIcon('mimetypes-other-other', IconSize::MEDIUM, 'overlay-missing')
|
||||
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing') . ' ' . $fileObject->getName())
|
||||
->render();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Preview web image or media elements
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']
|
||||
&& ($fileReferenceObject->getOriginalFile()->isImage() || $fileReferenceObject->getOriginalFile()->isMediaFile())
|
||||
) {
|
||||
$cropVariantCollection = CropVariantCollection::create((string)$fileReferenceObject->getProperty('crop'));
|
||||
$cropArea = $cropVariantCollection->getCropArea();
|
||||
$processingConfiguration = [
|
||||
'maxWidth' => 64,
|
||||
'maxHeight' => 64,
|
||||
];
|
||||
if (!$cropArea->isEmpty()) {
|
||||
$processingConfiguration = [
|
||||
'maxWidth' => 64,
|
||||
'maxHeight' => 64,
|
||||
'crop' => $cropArea->makeAbsoluteBasedOnFile($fileReferenceObject),
|
||||
];
|
||||
}
|
||||
$processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
|
||||
$attributes = [
|
||||
'src' => $processedImage->getPublicUrl() ?? '',
|
||||
'width' => $processedImage->getProperty('width'),
|
||||
'height' => $processedImage->getProperty('height'),
|
||||
'alt' => $fileReferenceObject->getAlternative() ?: $fileReferenceObject->getName(),
|
||||
'loading' => 'lazy',
|
||||
];
|
||||
$thumbData[] = '<img ' . GeneralUtility::implodeAttributes($attributes, true) . '/>';
|
||||
} else {
|
||||
$thumbData[] = $this->iconFactory->getIconForResource($fileObject)->setTitle($fileObject->getName())->render();
|
||||
}
|
||||
}
|
||||
|
||||
if ($thumbData !== []) {
|
||||
$result = '';
|
||||
foreach ($thumbData as $thumbDataItem) {
|
||||
$result .= '<div class="preview-thumbnails-element"><div class="preview-thumbnails-element-image">' . $thumbDataItem . '</div></div>';
|
||||
}
|
||||
return '<div class="preview-thumbnails">' . $result . '</div>';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a link on the input string and possibly a big button after the string which links to editing in the
|
||||
* RTE. Used for content element content displayed so the user can click the content / "Edit in Rich Text Editor"
|
||||
* button
|
||||
*
|
||||
* @param string $linkText String to link. Must be prepared for HTML output.
|
||||
* @return string If the whole thing was editable and $linkText is not empty $linkText is returned with the link
|
||||
* around. Otherwise just $linkText.
|
||||
*/
|
||||
public function linkToEditForm(string $linkText, RecordInterface $record, ServerRequestInterface $request): string
|
||||
{
|
||||
if ($linkText === '') {
|
||||
return $linkText;
|
||||
}
|
||||
$table = $record->getMainType();
|
||||
$backendUser = $this->getBackendUser();
|
||||
if ($backendUser->check('tables_modify', $table)
|
||||
&& $backendUser->checkRecordEditAccess($table, $record)->isAllowed
|
||||
&& (new Permission($backendUser->calcPerms(BackendUtility::getRecord('pages', $record->getPid()) ?? [])))->editContentPermissionIsGranted()
|
||||
) {
|
||||
$returnUrl = $request->getAttribute('normalizedParams')->getRequestUri() . '#element-' . $table . '-' . $record->getUid();
|
||||
$editParams = [
|
||||
'edit' => [$table => [$record->getUid() => 'edit']],
|
||||
'returnUrl' => $returnUrl,
|
||||
];
|
||||
return '<typo3-backend-contextual-record-edit-trigger'
|
||||
. ' url="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('record_edit_contextual', $editParams)) . '"'
|
||||
. ' edit-url="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('record_edit', $editParams)) . '"'
|
||||
. ' title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:edit')) . '"'
|
||||
. '>' . $linkText . '</typo3-backend-contextual-record-edit-trigger>';
|
||||
}
|
||||
return $linkText;
|
||||
}
|
||||
|
||||
private function getItemLabels(RecordInterface $record): array
|
||||
{
|
||||
$mainType = $record->getMainType();
|
||||
$cacheIdentifier = 'recordfieldpreviewprocessor-' . $mainType;
|
||||
$itemLabels = $this->runtimeCache->get($cacheIdentifier);
|
||||
if ($itemLabels === false) {
|
||||
$itemLabels = [];
|
||||
foreach ($this->schemaFactory->get($mainType)->getFields() as $field) {
|
||||
$itemLabels[$field->getName()] = $this->getLanguageService()->sL($field->getLabel());
|
||||
}
|
||||
$this->runtimeCache->set($cacheIdentifier, $itemLabels);
|
||||
}
|
||||
return $itemLabels;
|
||||
}
|
||||
|
||||
private function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user