public function GeneralFileLinkFormatter::viewElements in Formatter Suite 8
Builds a renderable array for a field value.
Parameters
\Drupal\Core\Field\FieldItemListInterface $items: The field values to be rendered.
string $langcode: The language that should be used to render the field.
Return value
array A renderable array for $items, as an array of child elements keyed by consecutive numeric indexes starting from 0.
Overrides FormatterInterface::viewElements
File
- src/
Plugin/ Field/ FieldFormatter/ GeneralFileLinkFormatter.php, line 701
Class
- GeneralFileLinkFormatter
- Formats a file field as one or more links.
Namespace
Drupal\formatter_suite\Plugin\Field\FieldFormatterCode
public function viewElements(FieldItemListInterface $items, $langcode) {
if ($items
->isEmpty() === TRUE) {
return [];
}
$this
->sanitizeSettings();
$showSize = $this
->getSetting('showSize');
$showIcon = $this
->getSetting('showIcon');
$showLink = $this
->getSetting('showLink');
$entity = $items
->getEntity();
// Prepare custom classes, if any.
$addClasses = $this
->getSetting('classes');
if (empty($addClasses) === TRUE) {
$addClasses = [];
}
else {
// Security: Class names are entered by an administrator.
// They may not include anything but CSS-compatible words, and
// certainly no HTML.
//
// Here, the class text is stripped of HTML tags as a start.
// A regex tosses unacceptable characters in a CSS class name.
$addClasses = strip_tags($addClasses);
$addClasses = mb_ereg_replace('[^_a-zA-Z0-9- ]', '', $addClasses);
if ($addClasses === FALSE) {
$addClasses = [];
}
$addClasses = explode(' ', $addClasses);
}
$elements = [];
$storage = $this->entityTypeManager
->getStorage('file');
foreach ($items as $delta => $item) {
//
// Get the file, URL, size, and MIME type.
// ---------------------------------------
// Get the File entity indicated by the file field. If the entity
// fails to load, something has become disconnected.
$fileId = $item->target_id;
$file = $storage
->load($fileId);
if ($file === NULL) {
$url = Url::fromRoute('<none>');
$fileSize = 0;
$mime = 'application/octet-stream';
$filename = $this
->t('(Missing file @id)', [
'@id' => $fileId,
]);
}
else {
$url = Url::fromUri(file_create_url($file
->getFileUri()));
$fileSize = $file
->getSize();
$mime = $file
->getMimeType();
$filename = $file
->getFilename();
}
// Format the file size.
// - Kilo/mega/giga with a "K" unit of 1000.
// - Use abbreviatios, not full words.
// - Use 3 digits.
if ($showSize === TRUE) {
$fileSizeMarkup = ' (' . Utilities::formatBytes($fileSize, 1000, FALSE, 3) . ')';
}
else {
$fileSizeMarkup = '';
}
// Add MIME-type based icon to classes.
$classes = $addClasses;
if ($showIcon === TRUE) {
$classes[] = 'file';
$classes[] = 'file--mime-' . strtr($mime, [
'/' => '-',
'.' => '-',
]);
$classes[] = 'file--' . file_icon_class($mime);
}
//
// Get the link title.
// -------------------
// Use custom text, text from the link, or the file name. If custom
// text is selected, but there isn't any, fall through to text from
// the link. If there is none of that, fall through to the file name.
switch ($this
->getSetting('titleStyle')) {
case 'text_custom':
// Security: A custom title is entered by an administrator.
// It may legitimately include HTML entities and minor HTML, but
// it should not include dangerous HTML.
//
// Because it may include HTML, we cannot pass it directly as
// the '#title' on a link, which will escape the HTML.
//
// We therefore use an Xss admin filter to remove any egreggious
// HTML (such as scripts and styles), and then FormattableMarkup()
// to mark the resulting text as safe.
$title = Xss::filterAdmin($this
->getSetting('titleCustomText'));
if (empty($title) === FALSE) {
$title .= $fileSizeMarkup;
$title = new FormattableMarkup($title, []);
break;
}
// Fall-through and use the field's description as the title text.
default:
case 'text_from_link':
// Security: Description text from the link field is entered by
// a user. It may legitimately include HTML entities and minor
// HTML, but it should not include dangerous HTML.
//
// Because it may include HTML, we cannot pass it directly as the
// '#title' on a link, which will escape the HTML.
//
// We therefore use an Xss strict filter to remove any egreggious
// HTML (such as scripts and styles, broken HTML, etc), and then
// FormattableMarkup() to mark the resulting text as safe.
$title = Xss::filter($item->description);
if (empty($title) === FALSE) {
$title .= $fileSizeMarkup;
$title = new FormattableMarkup($title, []);
break;
}
// Fall-through and use the filename as the title text.
case 'text_from_filename':
// Security: Filenames from the File entity are entered by a
// user. They may include any characters that the underlying
// file syste supports, which includes HTML '<', '>', etc.
//
// Passing this text as '#title' on a link will automatically
// escape special characters and insure they do not cause harm.
// We therefore don't need to do any special filtering here.
$title = (string) $filename;
$title .= $fileSizeMarkup;
break;
}
//
// Build the link.
// ---------------
// If the link is disabled, show the title text within a <span>.
// Otherwise, build a URL and create a link.
if ($showLink === FALSE) {
$elements[$delta] = [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $title,
'#attached' => [
'library' => [
'file/drupal.file',
],
],
'#attributes' => [
'class' => $classes,
],
'#cache' => [
'tags' => $entity
->getCacheTags(),
],
];
}
else {
$rel = '';
$target = '';
$download = FALSE;
$urlOptions = [];
switch ($this
->getSetting('openLinkIn')) {
case '_self':
$target = '_self';
break;
case '_blank':
$target = '_blank';
break;
case 'download':
$download = TRUE;
break;
}
$topic = $this
->getSetting('linkTopic');
if ($topic !== 'any') {
$rel .= $topic;
}
if (empty($rel) === FALSE) {
$urlOptions['attributes']['rel'] = $rel;
}
if (empty($target) === FALSE) {
$urlOptions['attributes']['target'] = $target;
}
if ($download === TRUE) {
$urlOptions['attributes']['download'] = '';
}
$url
->setOptions($urlOptions);
$elements[$delta] = [
'#type' => 'link',
'#title' => $title,
'#options' => $url
->getOptions(),
'#url' => $url,
'#attached' => [
'library' => [
'file/drupal.file',
],
],
'#attributes' => [
'class' => $classes,
'type' => $mime . '; length=' . $fileSize,
'title' => $filename,
],
'#cache' => [
'tags' => $entity
->getCacheTags(),
],
];
if (empty($items[$delta]->_attributes) === FALSE) {
// There are item attributes. Add them to the link's options.
$elements[$delta]['#options'] += [
'attributes' => $items[$delta]->_attributes,
];
// And remove them from the item since they are now included
// on the link.
unset($items[$delta]->_attributes);
}
}
}
if (empty($elements) === TRUE) {
return [];
}
//
// Add multi-value field processing.
// ---------------------------------
// If the field has multiple values, redirect to a theme and pass
// the list style and separator.
$isMultiple = $this->fieldDefinition
->getFieldStorageDefinition()
->isMultiple();
if ($isMultiple === TRUE) {
// Replace the 'field' theme with one that supports lists.
$elements['#theme'] = 'formatter_suite_field_list';
// Set the list style.
$elements['#list_style'] = $this
->getSetting('listStyle');
// Set the list separator.
//
// Security: The list separator is entered by an administrator.
// It may legitimately include HTML entities and minor HTML, but
// it should not include dangerous HTML.
//
// Because it may include HTML, we cannot pass it as-is and let a TWIG
// template use {{ }}, which will process the text and corrupt any
// entered HTML or HTML entities.
//
// We therefore use an Xss admin filter to remove any egreggious HTML
// (such as scripts and styles), and then FormattableMarkup() to mark the
// resulting text as safe.
$listSeparator = Xss::filterAdmin($this
->getSetting('listSeparator'));
$elements['#list_separator'] = new FormattableMarkup($listSeparator, []);
}
return $elements;
}