acquia_dam-1.0.0-rc1/src/Plugin/Field/FieldWidget/EntityRevisionAssetMediaLibrary.php
src/Plugin/Field/FieldWidget/EntityRevisionAssetMediaLibrary.php
<?php namespace Drupal\acquia_dam\Plugin\Field\FieldWidget; use Drupal\acquia_dam\Plugin\media\Source\Asset; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\OpenModalDialogCommand; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\media\Entity\Media; use Drupal\media\MediaInterface; use Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget; /** * Implementation of 'entity_reference_revisions_asset_media_library' widget. * * @FieldWidget( * id = "entity_reference_revisions_asset_media_library", * label = @Translation("Entity Revision Asset Media Library"), * description = @Translation("An media library asset field for revisions"), * multiple_values = TRUE, * field_types = { * "entity_reference_revisions" * } * ) */ class EntityRevisionAssetMediaLibrary extends MediaLibraryWidget { /** * {@inheritDoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element = parent::formElement($items, $delta, $element, $form, $form_state); $referenced_entities = $items->referencedEntities(); $field_name = $this->fieldDefinition->getName(); $request_values = \Drupal::request()->request->all($items->getName()); // New values are updated only in the request form. if ($request_values && array_key_exists('selection', $request_values)) { $this->checkForRevisionUpdates($referenced_entities, $request_values); } if ($referenced_entities) { $entity_ids = array_map(function ($entity) { return $entity->id(); }, $referenced_entities); $result = \Drupal::entityQuery('media') ->condition('mid', $entity_ids, 'IN') ->accessCheck()->execute(); $vid_list = array_keys($result); $current_time = \Drupal::time()->getCurrentTime(); foreach ($referenced_entities as $key => $entity) { assert($entity instanceof MediaInterface); $revision_id = $entity->getRevisionId(); $id = $entity->id(); $element['selection'][$key]['target_revision_id'] = [ '#type' => 'hidden', '#value' => $revision_id, ]; // This is much efficient than isLatestRevision when it comes to // multiple selections. if (!in_array($revision_id, $vid_list)) { $latest_entity = Media::load($entity->id()); if (!$latest_entity->getSource() instanceof Asset) { continue; } $expiry_date = $latest_entity->get('acquia_dam_expiry_date')->getValue(); if ($expiry_date && (int) $expiry_date[0]['value'] < $current_time) { continue; } $element['selection'][$key]['new_update'] = [ '#type' => 'button', '#value' => $this->t('Update Media'), '#name' => $field_name . '_update_media_' . $id, '#ajax' => [ 'callback' => [static::class, 'openUpdateDialog'], 'progress' => [ 'type' => 'throbber', 'message' => $this->t('Opening update form dialog.'), ], // The AJAX system automatically moves focus to the first tabbable // element of the modal, so we need to disable refocus on the // button. 'disable-refocus' => TRUE, ], ]; } } } $parents = $form['#parents']; $id_suffix = $parents ? '-' . implode('-', $parents) : ''; $field_widget_id = implode(':', array_filter([$field_name, $id_suffix])); // Revision field containing the id to update the widget accordingly. $element['media_library_selection_revision'] = [ '#type' => 'hidden', '#attributes' => [ // This is used to pass the selection from the modal to the widget. 'data-media-library-widget-value' => $field_widget_id . '_revisions', ], ]; return $element; } /** * AJAX callback to open the library modal. * * @param array $form * The form array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * * @return \Drupal\Core\Ajax\AjaxResponse * An AJAX response to open the update dialog. */ public static function openUpdateDialog(array $form, FormStateInterface $form_state) { $triggering_element = $form_state->getTriggeringElement(); $element = NestedArray::getValue($form, array_slice($triggering_element['#array_parents'], 0, -1)); $library_ui = \Drupal::formBuilder()->getForm('Drupal\acquia_dam\Form\FieldMediaRevisionDialog', $element['target_revision_id']['#value'], $element['target_id']['#value'], $element['#attributes']['data-drupal-selector'], $element['#parents'][0]); $dialog_options = [ 'dialogClass' => 'update-widget-modal', 'title' => t('Update Media'), 'height' => '75%', 'width' => '75%', ]; return (new AjaxResponse()) ->addCommand(new OpenModalDialogCommand($dialog_options['title'], $library_ui, $dialog_options)); } /** * {@inheritDoc} */ public static function updateWidget(array $form, FormStateInterface $form_state) { $response = parent::updateWidget($form, $form_state); $triggering_element = $form_state->getTriggeringElement(); // This callback is either invoked from the remove button or the update // button, which have different nesting levels. $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); // Clear the revision field along with the id. $element['media_library_selection_revision']['#value'] = ''; $wrapper_id = $triggering_element['#ajax']['wrapper']; $response->addCommand(new ReplaceCommand("#$wrapper_id", $element)); return $response; } /** * {@inheritDoc} */ 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)) { // Get the weight of the last items and count from there. $last_element = end($field_state['items']); $weight = $last_element ? $last_element['weight'] : 0; foreach ($media as $media_item) { // Any ID can be passed to the widget, so we have to check access. if ($media_item->access('view')) { $field_state['items'][] = [ 'target_revision_id' => $media_item->getRevisionId(), 'weight' => ++$weight, ]; } } static::setFieldState($element, $form_state, $field_state); } $form_state->setRebuild(); } /** * To check if the added entities are update manually updated by the user. * * @param array $referenced_entities * The array of referenced entities. * @param array $results * Array containing the field values. */ public function checkForRevisionUpdates(array &$referenced_entities, array $results) { $cleaned_selections = []; foreach ($results['selection'] as $selection) { $cleaned_selections[$selection['target_id']] = $selection['target_revision_id']; } foreach ($referenced_entities as $key => $entity) { assert($entity instanceof MediaInterface); $current_id = $entity->getRevisionId(); if ($cleaned_selections[$entity->id()] && $cleaned_selections[$entity->id()] != $current_id) { // Loading the latest media entity. $referenced_entities[$key] = Media::load($entity->id()); } } } }