View source
<?php
namespace Drupal\entity_browser\Plugin\Field\FieldWidget;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\SortArray;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\entity_browser\FieldWidgetDisplayManager;
use Drupal\image\Entity\ImageStyle;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Utility\Environment;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
class FileBrowserWidget extends EntityReferenceBrowserWidget {
protected static $deleteDepth = 3;
protected $items;
protected $configFactory;
protected $displayRepository;
protected $mimeTypeGuesser;
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager, FieldWidgetDisplayManager $field_display_manager, ConfigFactoryInterface $config_factory, EntityDisplayRepositoryInterface $display_repository, ModuleHandlerInterface $module_handler, AccountInterface $current_user, MimeTypeGuesserInterface $mime_type_guesser, MessengerInterface $messenger) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings, $entity_type_manager, $field_display_manager, $module_handler, $current_user, $messenger);
$this->entityTypeManager = $entity_type_manager;
$this->fieldDisplayManager = $field_display_manager;
$this->configFactory = $config_factory;
$this->displayRepository = $display_repository;
$this->mimeTypeGuesser = $mime_type_guesser;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container
->get('entity_type.manager'), $container
->get('plugin.manager.entity_browser.field_widget_display'), $container
->get('config.factory'), $container
->get('entity_display.repository'), $container
->get('module_handler'), $container
->get('current_user'), $container
->get('file.mime_type.guesser'), $container
->get('messenger'));
}
public static function defaultSettings() {
$settings = parent::defaultSettings();
unset($settings['field_widget_display']);
unset($settings['field_widget_display_settings']);
$settings['view_mode'] = 'default';
$settings['preview_image_style'] = 'thumbnail';
return $settings;
}
public function settingsForm(array $form, FormStateInterface $form_state) {
$element = parent::settingsForm($form, $form_state);
$has_file_entity = $this->moduleHandler
->moduleExists('file_entity');
$element['field_widget_display']['#access'] = FALSE;
$element['field_widget_display_settings']['#access'] = FALSE;
$element['view_mode'] = [
'#title' => $this
->t('File view mode'),
'#type' => 'select',
'#default_value' => $this
->getSetting('view_mode'),
'#options' => $this->displayRepository
->getViewModeOptions('file'),
'#access' => $has_file_entity,
];
$element['preview_image_style'] = [
'#title' => $this
->t('Preview image style'),
'#type' => 'select',
'#options' => image_style_options(FALSE),
'#default_value' => $this
->getSetting('preview_image_style'),
'#description' => $this
->t('The preview image will be shown while editing the content. Only relevant if using the default file view mode.'),
'#weight' => 15,
'#access' => !$has_file_entity && $this->fieldDefinition
->getType() == 'image',
];
return $element;
}
public function settingsSummary() {
$summary = $this
->summaryBase();
$view_mode = $this
->getSetting('view_mode');
$image_style_setting = $this
->getSetting('preview_image_style');
if ($this->moduleHandler
->moduleExists('file_entity')) {
$preview_image_style = $this
->t('Preview with @view_mode', [
'@view_mode' => $view_mode,
]);
}
elseif ($this->fieldDefinition
->getType() == 'image' && ($image_style = ImageStyle::load($image_style_setting))) {
$preview_image_style = $this
->t('Preview image style: @style', [
'@style' => $image_style
->label(),
]);
}
else {
$preview_image_style = $this
->t('No preview image');
}
array_unshift($summary, $preview_image_style);
return $summary;
}
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$this->items = $items;
return parent::formElement($items, $delta, $element, $form, $form_state);
}
protected function displayCurrentSelection($details_id, array $field_parents, array $entities) {
$field_type = $this->fieldDefinition
->getType();
$field_settings = $this->fieldDefinition
->getSettings();
$field_machine_name = $this->fieldDefinition
->getName();
$file_settings = $this->configFactory
->get('file.settings');
$widget_settings = $this
->getSettings();
$view_mode = $widget_settings['view_mode'];
$can_edit = (bool) $widget_settings['field_widget_edit'];
$has_file_entity = $this->moduleHandler
->moduleExists('file_entity');
$delta = 0;
$order_class = $field_machine_name . '-delta-order';
$current = [
'#type' => 'table',
'#empty' => $this
->t('No files yet'),
'#attributes' => [
'class' => [
'entities-list',
],
],
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => $order_class,
],
],
];
if ($has_file_entity || $field_type == 'image' && !empty($widget_settings['preview_image_style'])) {
$current['#header'][] = $this
->t('Preview');
}
if (!$has_file_entity) {
$current['#header'][] = $this
->t('Filename');
}
$current['#header'][] = $this
->t('Metadata');
$current['#header'][] = [
'data' => $this
->t('Operations'),
'colspan' => 3,
];
$current['#header'][] = $this
->t('Order', [], [
'context' => 'Sort order',
]);
foreach ($entities as $entity) {
if (!$entity
->getEntityType()
->getFormClass('edit')) {
$edit_button_access = FALSE;
}
elseif ($has_file_entity) {
$edit_button_access = $can_edit && $entity
->access('update', $this->currentUser);
}
$replace_button_access = $this
->getSetting('field_widget_replace') && count($entities) === 1;
$entity_id = $entity
->id();
$description = '';
$display_field = $field_settings['display_default'];
$alt = '';
$title = '';
$weight = $delta;
$width = NULL;
$height = NULL;
foreach ($this->items as $item) {
if ($item->target_id == $entity_id) {
if ($field_type == 'file') {
$description = $item->description;
$display_field = $item->display;
}
elseif ($field_type == 'image') {
$alt = $item->alt;
$title = $item->title;
$width = $item->width;
$height = $item->height;
}
$weight = $item->_weight ?: $delta;
}
}
$current[$entity_id] = [
'#attributes' => [
'class' => [
'draggable',
],
'data-entity-id' => $entity
->getEntityTypeId() . ':' . $entity_id,
'data-row-id' => $delta,
],
];
if ($has_file_entity) {
$current[$entity_id]['display'] = $this->entityTypeManager
->getViewBuilder('file')
->view($entity, $view_mode);
}
elseif ($field_type == 'image' && !empty($widget_settings['preview_image_style'])) {
$uri = $entity
->getFileUri();
$current[$entity_id]['display'] = [
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $width,
'#height' => $height,
'#style_name' => $widget_settings['preview_image_style'],
'#uri' => $uri,
];
}
if (!$has_file_entity) {
$current[$entity_id]['filename'] = [
'#markup' => $entity
->label(),
];
}
$current[$entity_id] += [
'meta' => [
'display_field' => [
'#type' => 'checkbox',
'#title' => $this
->t('Include file in display'),
'#default_value' => (bool) $display_field,
'#access' => $field_type == 'file' && $field_settings['display_field'],
],
'description' => [
'#type' => $file_settings
->get('description.type'),
'#title' => $this
->t('Description'),
'#default_value' => $description,
'#size' => 45,
'#maxlength' => $file_settings
->get('description.length'),
'#description' => $this
->t('The description may be used as the label of the link to the file.'),
'#access' => $field_type == 'file' && $field_settings['description_field'],
],
'alt' => [
'#type' => 'textfield',
'#title' => $this
->t('Alternative text'),
'#default_value' => $alt,
'#size' => 45,
'#maxlength' => 512,
'#description' => $this
->t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
'#access' => $field_type == 'image' && $field_settings['alt_field'],
'#required' => $field_type == 'image' && $field_settings['alt_field_required'],
],
'title' => [
'#type' => 'textfield',
'#title' => $this
->t('Title'),
'#default_value' => $title,
'#size' => 45,
'#maxlength' => 1024,
'#description' => $this
->t('The title is used as a tool tip when the user hovers the mouse over the image.'),
'#access' => $field_type == 'image' && $field_settings['title_field'],
'#required' => $field_type == 'image' && $field_settings['title_field_required'],
],
],
'edit_button' => [
'#type' => 'submit',
'#value' => $this
->t('Edit'),
'#ajax' => [
'url' => Url::fromRoute('entity_browser.edit_form', [
'entity_type' => $entity
->getEntityTypeId(),
'entity' => $entity_id,
]),
'options' => [
'query' => [
'details_id' => $details_id,
],
],
],
'#attributes' => [
'data-entity-id' => $entity
->getEntityTypeId() . ':' . $entity
->id(),
'data-row-id' => $delta,
'class' => [
'edit-button',
],
],
'#access' => $edit_button_access,
],
'replace_button' => [
'#type' => 'submit',
'#value' => $this
->t('Replace'),
'#ajax' => [
'callback' => [
get_class($this),
'updateWidgetCallback',
],
'wrapper' => $details_id,
],
'#submit' => [
[
get_class($this),
'removeItemSubmit',
],
],
'#name' => $field_machine_name . '_replace_' . $entity_id . '_' . md5(json_encode($field_parents)),
'#limit_validation_errors' => [
array_merge($field_parents, [
$field_machine_name,
'target_id',
]),
],
'#attributes' => [
'data-entity-id' => $entity
->getEntityTypeId() . ':' . $entity
->id(),
'data-row-id' => $delta,
'class' => [
'replace-button',
],
],
'#access' => $replace_button_access,
],
'remove_button' => [
'#type' => 'submit',
'#value' => $this
->t('Remove'),
'#ajax' => [
'callback' => [
get_class($this),
'updateWidgetCallback',
],
'wrapper' => $details_id,
],
'#submit' => [
[
get_class($this),
'removeItemSubmit',
],
],
'#name' => $field_machine_name . '_remove_' . $entity_id . '_' . md5(json_encode($field_parents)),
'#limit_validation_errors' => [
array_merge($field_parents, [
$field_machine_name,
'target_id',
]),
],
'#attributes' => [
'data-entity-id' => $entity
->getEntityTypeId() . ':' . $entity
->id(),
'data-row-id' => $delta,
'class' => [
'remove-button',
],
],
'#access' => (bool) $widget_settings['field_widget_remove'],
],
'_weight' => [
'#type' => 'weight',
'#title' => $this
->t('Weight for row @number', [
'@number' => $delta + 1,
]),
'#title_display' => 'invisible',
'#delta' => count($entities),
'#default_value' => $weight,
'#attributes' => [
'class' => [
$order_class,
],
],
],
];
$current['#attached']['library'][] = 'entity_browser/file_browser';
$delta++;
}
return $current;
}
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
$ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
$return = [];
foreach ($ids as $id) {
$id = explode(':', $id)[1];
if (is_array($values['current']) && isset($values['current'][$id])) {
$item_values = [
'target_id' => $id,
'_weight' => $values['current'][$id]['_weight'],
];
if ($this->fieldDefinition
->getType() == 'file') {
if (isset($values['current'][$id]['meta']['description'])) {
$item_values['description'] = $values['current'][$id]['meta']['description'];
}
if ($this->fieldDefinition
->getSetting('display_field') && isset($values['current'][$id]['meta']['display_field'])) {
$item_values['display'] = $values['current'][$id]['meta']['display_field'];
}
}
if ($this->fieldDefinition
->getType() == 'image') {
if (isset($values['current'][$id]['meta']['alt'])) {
$item_values['alt'] = $values['current'][$id]['meta']['alt'];
}
if (isset($values['current'][$id]['meta']['title'])) {
$item_values['title'] = $values['current'][$id]['meta']['title'];
}
}
$return[] = $item_values;
}
}
usort($return, function ($a, $b) {
return SortArray::sortByKeyInt($a, $b, '_weight');
});
return array_values($return);
}
public function getFileValidators($upload = FALSE) {
$validators = [];
$settings = $this->fieldDefinition
->getSettings();
if ($upload) {
$max_filesize = Bytes::toInt(Environment::getUploadMaxSize());
if (!empty($settings['max_filesize'])) {
$max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
}
$validators['file_validate_size'] = [
$max_filesize,
];
}
if ($this->fieldDefinition
->getType() == 'image') {
$supported_extensions = [
'png',
'gif',
'jpg',
'jpeg',
];
$extensions = isset($settings['file_extensions']) ? $settings['file_extensions'] : implode(' ', $supported_extensions);
$extensions = array_intersect(explode(' ', $extensions), $supported_extensions);
$validators['file_validate_extensions'] = [
implode(' ', $extensions),
];
if (!empty($settings['max_resolution']) || !empty($settings['min_resolution'])) {
$validators['entity_browser_file_validate_image_resolution'] = [
$settings['max_resolution'],
$settings['min_resolution'],
];
}
}
elseif (!empty($settings['file_extensions'])) {
$validators['file_validate_extensions'] = [
$settings['file_extensions'],
];
}
return $validators;
}
protected function getPersistentData() {
$data = parent::getPersistentData();
$settings = $this->fieldDefinition
->getSettings();
$data['validators']['file'] = [
'validators' => $this
->getFileValidators(),
];
$data['widget_context']['upload_location'] = $settings['uri_scheme'] . '://' . $settings['file_directory'];
$data['widget_context']['upload_validators'] = $this
->getFileValidators(TRUE);
$mimetypes = [];
foreach (explode(' ', $settings['file_extensions']) as $extension) {
if ($guess = $this->mimeTypeGuesser
->guess('file.' . $extension)) {
$mimetypes[] = $guess;
}
}
$data['widget_context']['target_file_mimetypes'] = $mimetypes;
return $data;
}
}