View source
<?php
namespace Drupal\gridstack\Plugin\gridstack\stylizer;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AnnounceCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\media\Entity\Media;
use Drupal\media_library\MediaLibraryState;
use Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget;
use Drupal\gridstack\GridStackDefault;
class MediaForm extends Style {
protected $fieldSettings = [];
protected $responsiveImageOptions;
protected $entity;
protected $fieldDefinition;
protected $targetEntityType;
protected $bundle;
protected $fieldDefinitions;
protected function setFieldSettings(array $settings = []) {
$this->fieldSettings = $settings;
return $this;
}
protected function setEntity($entity) {
$this->entity = $entity;
return $this;
}
public function getEntity() {
return $this->entity;
}
public function getFieldDefinition($name = '') {
if (!isset($this->fieldDefinition)) {
$this
->setFieldName($name);
if ($definitions = $this
->getFieldDefinitions()) {
$this->fieldDefinition = isset($definitions[$name]) ? $definitions[$name] : [];
}
else {
$this->fieldDefinition = $this
->getFieldData($name);
}
}
return $this->fieldDefinition;
}
public function getFieldCardinality($name = '') {
if ($definition = $this
->getFieldDefinition($name)) {
return $definition
->getFieldStorageDefinition()
->getCardinality();
}
return -1;
}
public function getFieldSettings($name = '') {
return empty($name) ? $this->fieldSettings : $this
->getFieldData($name, 'settings');
}
public function getLayoutFieldOptions() {
return $this
->getFieldData('', 'options');
}
public function getMediaLibraryTheme() {
$admin_theme = $this->manager
->configLoad('admin', 'system.theme');
if ($admin_theme == 'seven') {
return 'seven/media_library';
}
elseif ($admin_theme == 'claro') {
return 'claro/media_library.theme';
}
return FALSE;
}
public function getEntityData($entity_form) {
$extras = [];
$id = NULL;
$bundle = NULL;
$entity = NULL;
$target = NULL;
$mode = NULL;
if (method_exists($entity_form, 'getSectionStorage') && ($storage = $entity_form
->getSectionStorage())) {
$contexts = $storage
->getContextValues();
if (isset($contexts['entity']) && ($entity = $contexts['entity'])) {
$id = $entity
->id();
$bundle = $entity
->bundle();
$target = $entity
->getEntityTypeId();
$mode = $contexts['view_mode'];
$this
->setEntity($entity);
}
elseif (isset($contexts['display']) && ($display = $contexts['display'])) {
$id = $display
->id();
$bundle = $display
->getTargetBundle();
$target = $display
->getTargetEntityTypeId();
$mode = $contexts['view_mode'];
}
}
elseif (method_exists($entity_form, 'getEntity') && ($entity = $entity_form
->getEntity())) {
$id = $entity
->id();
$bundle = $entity
->getTargetBundle();
$target = $entity
->getTargetEntityTypeId();
$mode = $entity
->getMode();
$this
->setEntity($entity);
}
if ($bundle) {
$extras = [
'entity' => $entity,
'bundle' => $bundle,
'entity_id' => $id,
'entity_type_id' => $target,
'view_mode' => $mode,
];
$this->targetEntityType = $target;
$this->bundle = $bundle;
}
return $extras;
}
protected function saveMediaId(array $settings, FormStateInterface $form_state) {
$mid = isset($settings['target_id']) ? $settings['target_id'] : '';
return empty($settings['media_library_selection']) ? $mid : $settings['media_library_selection'];
}
protected function getMediaData($media, $mid = '') {
$data = [];
if ($mid && is_null($media)) {
$media = Media::load($mid);
}
if ($media) {
$data['media_id'] = $media
->id();
$data['media_bundle'] = $media
->bundle();
$data['media_source'] = $media
->getSource()
->getPluginId();
$data['source_field'] = $media
->getSource()
->getConfiguration()['source_field'];
$source = $media
->getSource();
$plugin_definition = $source
->getPluginDefinition();
if ($uri = $source
->getMetadata($media, $plugin_definition['thumbnail_uri_metadata_attribute'])) {
$data['uri'] = $uri;
$data['image_url'] = \file_url_transform_relative(\file_create_url($uri));
}
}
return $data;
}
public function getResponsiveImageOptions() {
if (!isset($this->responsiveImageOptions)) {
$options = [];
if ($this->manager
->getModuleHandler()
->moduleExists('responsive_image')) {
$image_styles = $this->manager
->entityLoadMultiple('responsive_image_style');
if (!empty($image_styles)) {
foreach ($image_styles as $name => $image_style) {
if ($image_style
->hasImageStyleMappings()) {
$options[$name] = Html::escape($image_style
->label());
}
}
}
}
$this->responsiveImageOptions = $options;
}
return $this->responsiveImageOptions;
}
protected function mediaElement(array &$element, $optionset, FormStateInterface $form_state, array $settings, array $extras = []) {
if (empty($settings['field_name'])) {
return;
}
$data = [];
$media = NULL;
$context = $settings['_scope'];
$delta = $settings['_delta'];
$main = [
'layout_settings',
'settings',
'styles',
];
$extra = [
'layout_settings',
'regions',
$context,
'styles',
];
$parents = $context == GridStackDefault::ROOT ? $main : $extra;
$remaining = 1;
$field_name = $settings['field_name'];
$id_suffix = $parents ? '-' . implode('-', $parents) : '';
$field_widget_id = implode(':', array_filter([
$field_name,
$id_suffix,
]));
$wrapper_id = $field_name . '-media-library-wrapper' . $id_suffix;
$view_builder = $this->manager
->getEntityTypeManager()
->getViewBuilder('media');
$cardinality = $this
->getFieldCardinality($field_name);
$allowed_media_type_ids = [
'image',
'remote_video',
'video',
];
$selected_type_id = reset($allowed_media_type_ids);
$limit_validation_errors = [
array_merge($parents, [
$field_name,
]),
];
$opener_parameters = [
'field_widget_id' => $field_widget_id,
'entity_type_id' => $extras['entity_type_id'],
'bundle' => $extras['bundle'],
'field_name' => $field_name,
];
$state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_media_type_ids, $selected_type_id, $remaining, $opener_parameters);
$form_state
->set('media_library_state', $state);
$add = $this
->t('Add media');
$mid = $this
->saveMediaId($settings, $form_state);
$target_bundles = isset($this
->getFieldSettings($field_name)['target_bundles']) ? $this
->getFieldSettings($field_name)['target_bundles'] : [];
$element += [
'#type' => 'details',
'#open' => TRUE,
'#tree' => TRUE,
'#title' => $this
->t('Styles'),
'#cardinality' => $cardinality,
'#delta' => $delta,
'#target_bundles' => $target_bundles,
'#attributes' => [
'id' => $wrapper_id,
'class' => [
'js-media-library-widget',
'form-wrapper--styles',
],
],
'#attached' => [
'library' => [
'media_library/widget',
],
],
'#parents' => $parents,
];
if ($optionset
->isFramework()) {
$element['#description'] = $this
->t('Requires <code>Min height</code> at <code>Preset classes</code>, else collapsed.');
}
$new_settings = self::getUserInputValues($element, $form_state);
if ($new_settings) {
$new_mid = $new_settings['media_library_selection'];
if ($new_mid && $new_mid != $mid) {
$mid = $new_mid;
}
}
$element['media_library_selection'] = [
'#type' => 'hidden',
'#attributes' => [
'data-media-library-widget-value' => $field_widget_id,
],
];
if (empty($mid)) {
$element['#attributes']['class'][] = 'ig-gs-media-empty';
}
$element['selection'] = [
'#type' => 'container',
'#theme_wrappers' => [
'container__media_library_widget_selection',
],
'#attributes' => [
'class' => [
'js-media-library-selection',
],
],
'#prefix' => '<div class="form-wrapper form-wrapper--media">',
'#field_name' => $field_name,
];
$element['selection'][$delta] = [
'#theme' => 'media_library_item__widget',
'#attributes' => [
'class' => [
'js-media-library-item',
'form-wrapper--media__item',
],
'tabindex' => '-1',
'data-media-library-item-delta' => $delta,
],
];
$element['selection'][$delta]['#field_name'] = $field_name;
$element['selection'][$delta]['thumbnail'] = [];
if ($mid) {
$add = $this
->t('Replace media');
$media = Media::load($mid);
$data = $this
->getMediaData($media);
$element['selection'][$delta]['rendered_entity'] = $view_builder
->view($media, 'media_library');
$element['selection'][$delta]['remove_button'] = [
'#type' => 'submit',
'#name' => $field_name . '-' . $delta . '-media-library-remove-button' . $id_suffix,
'#value' => $this
->t('Remove'),
'#media_id' => $media
->id(),
'#attributes' => [
'aria-label' => $this
->t('Remove @label', [
'@label' => $media
->label(),
]),
'class' => [
'form-submit--gs-remove',
],
'title' => $this
->t('Remove'),
],
'#ajax' => [
'callback' => [
static::class,
'updateWidget',
],
'wrapper' => $wrapper_id,
'progress' => [
'type' => 'throbber',
'message' => $this
->t('Removing @label.', [
'@label' => $media
->label(),
]),
],
],
'#submit' => [
[
static::class,
'removeItem',
],
],
'#limit_validation_errors' => $limit_validation_errors,
];
}
$element['selection'][$delta]['target_id'] = [
'#type' => 'hidden',
'#default_value' => $mid,
'#attributes' => [
'data-gs-media-storage' => TRUE,
],
];
$element['selection'][$delta]['weight'] = [
'#type' => 'number',
'#theme' => 'input__number__media_library_item_weight',
'#title' => $this
->t('Weight'),
'#default_value' => $delta,
'#attributes' => [
'class' => [
'js-media-library-item-weight',
],
],
];
$element['open_button'] = [
'#type' => 'button',
'#value' => $add,
'#name' => $field_name . '-media-library-open-button' . $id_suffix,
'#attributes' => [
'class' => [
'js-media-library-open-button',
'form-submit--gs-add-replace',
],
'data-disable-refocus' => 'true',
],
'#media_library_state' => $state,
'#ajax' => [
'callback' => [
MediaLibraryWidget::class,
'openMediaLibrary',
],
'progress' => [
'type' => 'throbber',
'message' => $this
->t('Opening media library.'),
],
],
'#limit_validation_errors' => [],
'#attached' => [
'library' => [
'media_library/widget',
],
],
];
$element['media_library_update_widget'] = [
'#type' => 'submit',
'#value' => $this
->t('Update widget'),
'#name' => $field_name . '-media-library-update' . $id_suffix,
'#ajax' => [
'callback' => [
static::class,
'updateWidget',
],
'wrapper' => $wrapper_id,
'progress' => [
'type' => 'throbber',
'message' => $this
->t('Adding selection.'),
],
],
'#attributes' => [
'data-media-library-widget-update' => $field_widget_id,
'class' => [
'js-hide',
'visually-hidden',
],
],
'#submit' => [
[
static::class,
'addItems',
],
],
'#limit_validation_errors' => empty($mid) ? [] : $limit_validation_errors,
];
$element['metadata'] = [
'#type' => 'hidden',
'#default_value' => $data ? Json::encode($data) : '',
'#suffix' => '</div>',
];
}
public static function updateWidget(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
$wrapper_id = $triggering_element['#ajax']['wrapper'];
$is_remove_button = end($triggering_element['#parents']) === 'remove_button';
$length = $is_remove_button ? -3 : -1;
if (count($triggering_element['#array_parents']) < abs($length)) {
throw new \LogicException('The element that triggered the widget update was at an unexpected depth. Triggering element parents were: ' . implode(',', $triggering_element['#array_parents']));
}
$parents = array_slice($triggering_element['#array_parents'], 0, $length);
$element = NestedArray::getValue($form, $parents);
$element['media_library_selection']['#value'] = '';
if ($is_remove_button) {
$announcement = t('Media has been removed.');
unset($element['selection'][$element['#delta']]);
$element['open_button']['#value'] = t('Add media');
}
else {
$new_items = count(static::getNewMediaItems($element, $form_state));
$announcement = \Drupal::translation()
->formatPlural($new_items, 'Added one media item.', 'Added @count media items.');
}
$response = new AjaxResponse();
$response
->addCommand(new ReplaceCommand("#{$wrapper_id}", $element));
$response
->addCommand(new AnnounceCommand($announcement));
return $response;
}
public static function removeItem(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
if (count($triggering_element['#array_parents']) < 4) {
throw new \LogicException('Expected the remove button to be more than four levels deep in the form. Triggering element parents were: ' . implode(',', $triggering_element['#array_parents']));
}
$parents = array_slice($triggering_element['#array_parents'], 0, -3);
$element = NestedArray::getValue($form, $parents);
$path = $element['#parents'];
$values = NestedArray::getValue($form_state
->getValues(), $path);
$field_state = static::getFieldState($element, $form_state);
$delta = array_slice($triggering_element['#array_parents'], -2, 1)[0];
if (isset($values['selection'][$delta])) {
$field_state['removed_item_weight'] = $values['selection'][$delta]['weight'];
$field_state['removed_item_id'] = $triggering_element['#media_id'];
unset($values['selection'][$delta]);
$field_state['items'] = $values['selection'];
static::setFieldState($element, $form_state, $field_state);
}
$form_state
->setRebuild();
}
public static function addItems(array $form, FormStateInterface $form_state) {
$button = $form_state
->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
$field_state = static::getFieldState($element, $form_state);
$media = static::getNewMediaItems($element, $form_state);
if (!empty($media)) {
$last_element = end($field_state['items']);
$weight = $last_element ? $last_element['weight'] : 0;
foreach ($media as $media_item) {
if ($media_item
->access('view')) {
$field_state['items'][] = [
'target_id' => $media_item
->id(),
'weight' => ++$weight,
];
}
}
static::setFieldState($element, $form_state, $field_state);
}
$form_state
->setRebuild();
}
protected static function getUserInputValues(array $element, FormStateInterface $form_state) {
$path = isset($element['#parents']) ? $element['#parents'] : [];
return NestedArray::getValue($form_state
->getUserInput(), $path);
}
protected static function getNewMediaItems(array $element, FormStateInterface $form_state) {
$value = self::getUserInputValues($element, $form_state);
$mid = empty($value['media_library_selection']) ? '' : $value['media_library_selection'];
return $mid ? Media::loadMultiple([
$mid,
]) : [];
}
protected static function getFieldState(array $element, FormStateInterface $form_state) {
$values = self::getUserInputValues($element, $form_state);
$selection = isset($values['selection']) ? $values['selection'] : [];
$parents = isset($element['#parents']) ? $element['#parents'] : [];
$parents = isset($element['#field_parents']) ? $element['#field_parents'] : $parents;
$widget_state = MediaLibraryWidget::getWidgetState($parents, $element['#field_name'], $form_state);
$widget_state['items'] = isset($widget_state['items']) ? $widget_state['items'] : $selection;
return $widget_state;
}
protected static function setFieldState(array $element, FormStateInterface $form_state, array $field_state) {
$element['#field_parents'] = isset($element['#parents']) ? $element['#parents'] : [];
MediaLibraryWidget::setWidgetState($element['#field_parents'], $element['#field_name'], $form_state, $field_state);
}
protected function getFieldData($name = '', $key = '') {
$field_definitions = $this
->getFieldDefinitions();
if (empty($field_definitions)) {
return [];
}
$options = $definitions = $output = [];
foreach ($field_definitions as $field_definition) {
if ($field_definition
->getType() != 'entity_reference') {
continue;
}
$field_settings = $field_definition
->getSettings();
$field_name = $field_definition
->getName();
if ($field_settings['handler'] == 'default:media') {
if ($field_definition
->getFieldStorageDefinition()
->getCardinality() !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
continue;
}
$options[$field_name] = $field_name;
$this
->setFieldSettings($field_settings['handler_settings']);
$definitions[$field_name] = $key == 'settings' && $name == $field_name ? $field_settings['handler_settings'] : $field_definition;
}
}
if ($definitions) {
$definitions = $name ? $definitions[$name] : $definitions;
$output = $key == 'options' ? $options : $definitions;
}
return $output;
}
protected function getFieldDefinitions() {
if (!isset($this->fieldDefinitions)) {
if ($this
->getEntity()) {
$this->fieldDefinitions = $this
->getEntity()
->getFieldDefinitions();
}
else {
$this->fieldDefinitions = empty($this->bundle) ? [] : \Drupal::service('entity_field.manager')
->getFieldDefinitions($this->targetEntityType, $this->bundle);
}
}
return $this->fieldDefinitions;
}
}