Verified Commit a0acceaa authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3059955 by oknate, Hardik_Patel_12, paulocs, phenaproxima, seanB,...

Issue #3059955 by oknate, Hardik_Patel_12, paulocs, phenaproxima, seanB, NikolaAt, smustgrave, bnjmnm, yogeshmpawar, rensingh99, xjm, chr.fritsch, lauriii, raman.b, Wim Leers, nikunj.shah, AndySipple, harshitthakore, saurabh.tripathi.cs, AaronMcHale, irene_dobbs, alexpott, andrewmacpherson, dorficus, effulgentsia, himanshu_sindhwani, codersukanta, pankaj.singh, tripurari, snehalgaikwad, priyanka.sahni: It is possible to overflow the number of items allowed in Media Library

(cherry picked from commit ae4b7247)
parent bc4ffef3
Loading
Loading
Loading
Loading
+13 −12
Original line number Diff line number Diff line
@@ -309,6 +309,17 @@
        $('.js-media-library-selected-count').html(selectItemsText);
      }

      function checkEnabled() {
        updateSelectionCount(settings.media_library.selection_remaining);
        if (
          currentSelection.length === settings.media_library.selection_remaining
        ) {
          disableItems($mediaItems.not(':checked'));
          enableItems($mediaItems.filter(':checked'));
        } else {
          enableItems($mediaItems);
        }
      }
      // Update the selection array and the hidden form field when a media item
      // is selected.
      $(once('media-item-change', $mediaItems)).on('change', (e) => {
@@ -345,7 +356,7 @@
            item.value = currentSelection.join();
          });
      });

      checkEnabled();
      // The hidden selection form field changes when the selection is updated.
      $(
        once(
@@ -353,17 +364,7 @@
          $form.find('#media-library-modal-selection'),
        ),
      ).on('change', (e) => {
        updateSelectionCount(settings.media_library.selection_remaining);

        // Prevent users from selecting more items than allowed.
        if (
          currentSelection.length === settings.media_library.selection_remaining
        ) {
          disableItems($mediaItems.not(':checked'));
          enableItems($mediaItems.filter(':checked'));
        } else {
          enableItems($mediaItems);
        }
        checkEnabled();
      });

      // Apply the current selection to the media library view. Changing the
+41 −2
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\FocusFirstCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -700,11 +701,22 @@ public function updateLibrary(array &$form, FormStateInterface $form_state) {
      return $media->id();
    }, $this->getAddedMediaItems($form_state));

    $selected_count = $this->getSelectedMediaItemCount($media_ids, $form_state);

    $response = new AjaxResponse();
    $response->addCommand(new UpdateSelectionCommand($media_ids));
    $media_id_to_focus = array_pop($media_ids);
    $response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $this->buildMediaLibraryUi($form_state)));
    $response->addCommand(new InvokeCommand("#media-library-content [value=$media_id_to_focus]", 'focus'));
    $available_slots = $this->getMediaLibraryState($form_state)->getAvailableSlots();
    if ($available_slots > 0 && $selected_count > $available_slots) {
      $warning = $this->formatPlural($selected_count - $available_slots, 'There are currently @total items selected. The maximum number of items for the field is @max. Please remove @count item from the selection.', 'There are currently @total items selected. The maximum number of items for the field is @max. Please remove @count items from the selection.', [
        '@total' => $selected_count,
        '@max' => $available_slots,
      ]);
      $response->addCommand(new MessageCommand($warning, '#media-library-messages', ['type' => 'warning']));
    }

    return $response;
  }

@@ -750,17 +762,44 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    // The added media items get an ID when they are saved in ::submitForm().
    // For that reason the added media items are keyed by delta in the form
    // state and we have to do an array map to get each media ID.
    $current_media_ids = array_map(function (MediaInterface $media) {
    $media_ids = array_map(function (MediaInterface $media) {
      return $media->id();
    }, $this->getCurrentMediaItems($form_state));

    // Allow the opener service to respond to the selection.
    $state = $this->getMediaLibraryState($form_state);
    $selected_count = $this->getSelectedMediaItemCount($media_ids, $form_state);

    $available_slots = $this->getMediaLibraryState($form_state)->getAvailableSlots();
    if ($available_slots > 0 && $selected_count > $available_slots) {
      // Return to library where we display a warning about the overage.
      return $this->updateLibrary($form, $form_state);
    }

    return $this->openerResolver->get($state)
      ->getSelectionResponse($state, $current_media_ids)
      ->getSelectionResponse($state, $media_ids)
      ->addCommand(new CloseDialogCommand());
  }

  /**
   * Get the number of selected media.
   *
   * @param array $media_ids
   *   Array with the media IDs.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return int
   *   The number of media currently selected.
   */
  private function getSelectedMediaItemCount(array $media_ids, FormStateInterface $form_state): int {
    $selected_count = count($media_ids);
    if ($current_selection = $form_state->getValue('current_selection')) {
      $selected_count += count(explode(',', $current_selection));
    }
    return $selected_count;
  }

  /**
   * Get the media library state from the form state.
   *
+5 −2
Original line number Diff line number Diff line
@@ -165,8 +165,11 @@ protected function buildInputElement(array $form, FormStateInterface $form_state
      // @todo Move validation in https://www.drupal.org/node/2988215
      '#process' => array_merge(['::validateUploadElement'], $process, ['::processUploadElement']),
      '#upload_validators' => $item->getUploadValidators(),
      '#multiple' => $slots > 1 || $slots === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      '#cardinality' => $slots,
      '#multiple' => TRUE,
      // Do not limit the number uploaded. There is validation based on the
      // number selected in the media library that prevents overages.
      // @see Drupal\media_library\Form\AddFormBase::updateLibrary()
      '#cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      '#remaining_slots' => $slots,
    ];

+23 −0
Original line number Diff line number Diff line
@@ -2,7 +2,9 @@

namespace Drupal\media_library\Plugin\views\field;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
@@ -46,6 +48,14 @@ public function render(ResultRow $values) {
   */
  public function viewsForm(array &$form, FormStateInterface $form_state) {
    $form['#attributes']['class'] = ['js-media-library-views-form'];
    // Add target for AJAX messages.
    $form['media_library_messages'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'media-library-messages',
      ],
      '#weight' => -10,
    ];

    // Add an attribute that identifies the media type displayed in the form.
    if (isset($this->view->args[0])) {
@@ -125,6 +135,19 @@ public static function updateWidget(array &$form, FormStateInterface $form_state
    // Allow the opener service to handle the selection.
    $state = MediaLibraryState::fromRequest($request);

    $current_selection = $form_state->getValue('media_library_select_form_selection');
    $available_slots = $state->getAvailableSlots();
    $selected_count = count(explode(',', $current_selection));
    if ($available_slots > 0 && $selected_count > $available_slots) {
      $response = new AjaxResponse();
      $error = \Drupal::translation()->formatPlural($selected_count - $available_slots, 'There are currently @total items selected. The maximum number of items for the field is @max. Please remove @count item from the selection.', 'There are currently @total items selected. The maximum number of items for the field is @max. Please remove @count items from the selection.', [
        '@total' => $selected_count,
        '@max' => $available_slots,
      ]);
      $response->addCommand(new MessageCommand($error, '#media-library-messages', ['type' => 'error']));
      return $response;
    }

    return \Drupal::service('media_library.opener_resolver')
      ->get($state)
      ->getSelectionResponse($state, $selected_ids)
+18 −2
Original line number Diff line number Diff line
@@ -354,17 +354,21 @@ protected function assertNoMediaAdded() {
   * @param string $expected_announcement
   *   (optional) The expected screen reader announcement once the modal is
   *   closed.
   * @param bool $should_close
   *   (optional) TRUE if we expect the modal to be successfully closed.
   *
   * @todo Consider requiring screen reader assertion every time "Insert
   *   selected" is pressed in
   *   https://www.drupal.org/project/drupal/issues/3087227.
   */
  protected function pressInsertSelected($expected_announcement = NULL) {
  protected function pressInsertSelected($expected_announcement = NULL, bool $should_close = TRUE) {
    $this->assertSession()
      ->elementExists('css', '.ui-dialog-buttonpane')
      ->pressButton('Insert selected');
    $this->waitForNoText('Add or select media');

    if ($should_close) {
      $this->waitForNoText('Add or select media');
    }
    if ($expected_announcement) {
      $this->waitForText($expected_announcement);
    }
@@ -400,6 +404,18 @@ protected function selectMediaItem($index, $expected_selected_count = NULL) {
    }
  }

  /**
   * De-selects an item in the media library modal.
   *
   * @param int $index
   *   The zero-based index of the item to unselect.
   */
  protected function deselectMediaItem(int $index): void {
    $checkboxes = $this->getCheckboxes();
    $this->assertGreaterThan($index, count($checkboxes));
    $checkboxes[$index]->uncheck();
  }

  /**
   * Switches to the grid display of the widget view.
   */
Loading