diff core/modules/media_library/src/MediaLibraryUiBuilder.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php	Thu May 09 15:34:47 2019 +0100
@@ -0,0 +1,330 @@
+<?php
+
+namespace Drupal\media_library;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+use Drupal\views\ViewExecutableFactory;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+
+/**
+ * Service which builds the media library.
+ *
+ * @internal
+ *   Media Library is an experimental module and its internal code may be
+ *   subject to change in minor releases. External code should not instantiate
+ *   or extend this class.
+ */
+class MediaLibraryUiBuilder {
+
+  use StringTranslationTrait;
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The currently active request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The views executable factory.
+   *
+   * @var \Drupal\views\ViewExecutableFactory
+   */
+  protected $viewsExecutableFactory;
+
+  /**
+   * Constructs a MediaLibraryUiBuilder instance.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
+   * @param \Drupal\views\ViewExecutableFactory $views_executable_factory
+   *   The views executable factory.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The currently active request object.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->request = $request_stack->getCurrentRequest();
+    $this->viewsExecutableFactory = $views_executable_factory;
+    $this->formBuilder = $form_builder;
+  }
+
+  /**
+   * Get media library dialog options.
+   *
+   * @return array
+   *   The media library dialog options.
+   */
+  public static function dialogOptions() {
+    return [
+      'dialogClass' => 'media-library-widget-modal',
+      'title' => t('Add or select media'),
+      'height' => '75%',
+      'width' => '75%',
+    ];
+  }
+
+  /**
+   * Build the media library UI.
+   *
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   (optional) The current state of the media library, derived from the
+   *   current request.
+   *
+   * @return array
+   *   The render array for the media library.
+   */
+  public function buildUi(MediaLibraryState $state = NULL) {
+    if (!$state) {
+      $state = MediaLibraryState::fromRequest($this->request);
+    }
+    // When navigating to a media type through the vertical tabs, we only want
+    // to load the changed library content. This is not only more efficient, but
+    // also provides a more accessible user experience for screen readers.
+    if ($state->get('media_library_content') === '1') {
+      return $this->buildLibraryContent($state);
+    }
+    else {
+      return [
+        '#type' => 'html_tag',
+        '#tag' => 'div',
+        '#attributes' => [
+          'id' => 'media-library-wrapper',
+          'class' => ['media-library-wrapper'],
+        ],
+        'menu' => $this->buildMediaTypeMenu($state),
+        'content' => $this->buildLibraryContent($state),
+        // Attach the JavaScript for the media library UI. The number of
+        // available slots needs to be added to make sure users can't select
+        // more items than allowed.
+        '#attached' => [
+          'library' => ['media_library/ui'],
+          'drupalSettings' => [
+            'media_library' => [
+              'selection_remaining' => $state->getAvailableSlots(),
+            ],
+          ],
+        ],
+      ];
+    }
+  }
+
+  /**
+   * Build the media library content area.
+   *
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   The current state of the media library, derived from the current request.
+   *
+   * @return array
+   *   The render array for the media library.
+   */
+  protected function buildLibraryContent(MediaLibraryState $state) {
+    return [
+      '#type' => 'html_tag',
+      '#tag' => 'div',
+      '#attributes' => [
+        'id' => 'media-library-content',
+        'class' => ['media-library-content'],
+        'tabindex' => -1,
+      ],
+      'form' => $this->buildMediaTypeAddForm($state),
+      'view' => $this->buildMediaLibraryView($state),
+    ];
+  }
+
+  /**
+   * Check access to the media library.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   (optional) Run access checks for this account.
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   (optional) The current state of the media library, derived from the
+   *   current request.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   *   The access result.
+   */
+  public function checkAccess(AccountInterface $account = NULL, MediaLibraryState $state = NULL) {
+    if (!$state) {
+      try {
+        MediaLibraryState::fromRequest($this->request);
+      }
+      catch (BadRequestHttpException $e) {
+        return AccessResult::forbidden($e->getMessage());
+      }
+      catch (\InvalidArgumentException $e) {
+        return AccessResult::forbidden($e->getMessage());
+      }
+    }
+    // Deny access if the view or display are removed.
+    $view = $this->entityTypeManager->getStorage('view')->load('media_library');
+    if (!$view) {
+      return AccessResult::forbidden('The media library view does not exist.')
+        ->setCacheMaxAge(0);
+    }
+    if (!$view->getDisplay('widget')) {
+      return AccessResult::forbidden('The media library widget display does not exist.')
+        ->addCacheableDependency($view);
+    }
+    return AccessResult::allowedIfHasPermission($account, 'view media')
+      ->addCacheableDependency($view);
+  }
+
+  /**
+   * Get the media type menu for the media library.
+   *
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   The current state of the media library, derived from the current request.
+   *
+   * @return array
+   *   The render array for the media type menu.
+   */
+  protected function buildMediaTypeMenu(MediaLibraryState $state) {
+    // Add the menu for each type if we have more than 1 media type enabled for
+    // the field.
+    $allowed_type_ids = $state->getAllowedTypeIds();
+    if (count($allowed_type_ids) <= 1) {
+      return [];
+    }
+
+    // @todo: Add a class to the li element.
+    //   https://www.drupal.org/project/drupal/issues/3029227
+    $menu = [
+      '#theme' => 'links',
+      '#links' => [],
+      '#attributes' => [
+        'class' => ['media-library-menu', 'js-media-library-menu'],
+      ],
+    ];
+
+    $allowed_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple($allowed_type_ids);
+
+    $selected_type_id = $state->getSelectedTypeId();
+    foreach ($allowed_types as $allowed_type_id => $allowed_type) {
+      $link_state = MediaLibraryState::create($state->getOpenerId(), $state->getAllowedTypeIds(), $allowed_type_id, $state->getAvailableSlots());
+      // Add the 'media_library_content' parameter so the response will contain
+      // only the updated content for the tab.
+      // @see self::buildUi()
+      $link_state->set('media_library_content', 1);
+
+      $title = $allowed_type->label();
+      if ($allowed_type_id === $selected_type_id) {
+        $title = [
+          '#markup' => $this->t('@title<span class="active-tab visually-hidden"> (active tab)</span>', ['@title' => $title]),
+        ];
+      }
+
+      $menu['#links']['media-library-menu-' . $allowed_type_id] = [
+        'title' => $title,
+        'url' => Url::fromRoute('media_library.ui', [], [
+          'query' => $link_state->all(),
+        ]),
+        'attributes' => [
+          'class' => ['media-library-menu__link'],
+        ],
+      ];
+    }
+
+    // Set the active menu item.
+    $menu['#links']['media-library-menu-' . $selected_type_id]['attributes']['class'][] = 'active';
+
+    return $menu;
+  }
+
+  /**
+   * Get the add form for the selected media type.
+   *
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   The current state of the media library, derived from the current request.
+   *
+   * @return array
+   *   The render array for the media type add form.
+   */
+  protected function buildMediaTypeAddForm(MediaLibraryState $state) {
+    $selected_type_id = $state->getSelectedTypeId();
+
+    if (!$this->entityTypeManager->getAccessControlHandler('media')->createAccess($selected_type_id)) {
+      return [];
+    }
+
+    $selected_type = $this->entityTypeManager->getStorage('media_type')->load($selected_type_id);
+    $plugin_definition = $selected_type->getSource()->getPluginDefinition();
+
+    if (empty($plugin_definition['forms']['media_library_add'])) {
+      return [];
+    }
+
+    // After the form to add new media is submitted, we need to rebuild the
+    // media library with a new instance of the media add form. The form API
+    // allows us to do that by forcing empty user input.
+    // @see \Drupal\Core\Form\FormBuilder::doBuildForm()
+    $form_state = new FormState();
+    if ($state->get('_media_library_form_rebuild')) {
+      $form_state->setUserInput([]);
+      $state->remove('_media_library_form_rebuild');
+    }
+    $form_state->set('media_library_state', $state);
+    return $this->formBuilder->buildForm($plugin_definition['forms']['media_library_add'], $form_state);
+  }
+
+  /**
+   * Get the media library view.
+   *
+   * @param \Drupal\media_library\MediaLibraryState $state
+   *   The current state of the media library, derived from the current request.
+   *
+   * @return array
+   *   The render array for the media library view.
+   */
+  protected function buildMediaLibraryView(MediaLibraryState $state) {
+    // @todo Make the view configurable in
+    //   https://www.drupal.org/project/drupal/issues/2971209
+    $view = $this->entityTypeManager->getStorage('view')->load('media_library');
+    $view_executable = $this->viewsExecutableFactory->get($view);
+    $display_id = 'widget';
+
+    // Make sure the state parameters are set in the request so the view can
+    // pass the parameters along in the pager, filters etc.
+    $view_request = $view_executable->getRequest();
+    $view_request->query->add($state->all());
+    $view_executable->setRequest($view_request);
+
+    $args = [$state->getSelectedTypeId()];
+
+    // Make sure the state parameters are set in the request so the view can
+    // pass the parameters along in the pager, filters etc.
+    $request = $view_executable->getRequest();
+    $request->query->add($state->all());
+    $view_executable->setRequest($request);
+
+    $view_executable->setDisplay($display_id);
+    $view_executable->preExecute($args);
+    $view_executable->execute($display_id);
+
+    return $view_executable->buildRenderable($display_id, $args, FALSE);
+  }
+
+}