Unverified Commit 7b11e5f4 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3038254 by phenaproxima, seanB, Wim Leers, yogeshmpawar, effulgentsia,...

Issue #3038254 by phenaproxima, seanB, Wim Leers, yogeshmpawar, effulgentsia, alexpott, larowlan: Delegate media library access to the "thing" that opened the library

(cherry picked from commit 94361f33)
parent 1f8ae138
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
services:
  media_library.ui_builder:
    class: Drupal\media_library\MediaLibraryUiBuilder
    arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder']
    arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder', '@media_library.opener_resolver']
  media_library.route_subscriber:
    class: Drupal\media_library\Routing\RouteSubscriber
    tags:
@@ -12,3 +12,4 @@ services:
      - [setContainer, ['@service_container']]
  media_library.opener.field_widget:
    class: Drupal\media_library\MediaLibraryFieldWidgetOpener
    arguments: ['@entity_type.manager']
+86 −1
Original line number Diff line number Diff line
@@ -2,8 +2,12 @@

namespace Drupal\media_library;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Session\AccountInterface;

/**
@@ -11,11 +15,92 @@
 */
class MediaLibraryFieldWidgetOpener implements MediaLibraryOpenerInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * MediaLibraryFieldWidgetOpener constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function checkAccess(MediaLibraryState $state, AccountInterface $account) {
    throw new \Exception('Not yet implemented, see https://www.drupal.org/project/drupal/issues/3038254.');
    $parameters = $state->getOpenerParameters() + ['entity_id' => NULL];

    $process_result = function ($result) {
      if ($result instanceof RefinableCacheableDependencyInterface) {
        $result->addCacheContexts(['url.query_args']);
      }
      return $result;
    };

    // Forbid access if any of the required parameters are missing.
    foreach (['entity_type_id', 'bundle', 'field_name'] as $key) {
      if (empty($parameters[$key])) {
        return $process_result(AccessResult::forbidden("$key parameter is missing."));
      }
    }

    $entity_type_id = $parameters['entity_type_id'];
    $bundle = $parameters['bundle'];
    $field_name = $parameters['field_name'];

    // Since we defer to a field to determine access, ensure we are dealing with
    // a fieldable entity type.
    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
    if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
      throw new \LogicException("The media library can only be opened by fieldable entities.");
    }

    $storage = $this->entityTypeManager->getStorage($entity_type_id);
    $access_handler = $this->entityTypeManager->getAccessControlHandler($entity_type_id);

    if ($parameters['entity_id']) {
      $entity = $storage->load($parameters['entity_id']);
      $entity_access = $access_handler->access($entity, 'update', $account, TRUE);
    }
    else {
      $entity_access = $access_handler->createAccess($bundle, $account, [], TRUE);
    }

    // If entity-level access is denied, there's no point in continuing.
    if (!$entity_access->isAllowed()) {
      return $process_result($entity_access);
    }

    // If the entity has not been loaded, create it in memory now.
    if (!isset($entity)) {
      $values = [];
      if ($bundle_key = $entity_type->getKey('bundle')) {
        $values[$bundle_key] = $bundle;
      }
      /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
      $entity = $storage->create($values);
    }

    $items = $entity->get($field_name);
    $field_definition = $items->getFieldDefinition();

    if ($field_definition->getType() !== 'entity_reference') {
      throw new \LogicException('Expected the media library to be opened by an entity reference field.');
    }
    if ($field_definition->getFieldStorageDefinition()->getSetting('target_type') !== 'media') {
      throw new \LogicException('Expected the media library to be opened by an entity reference field that target media items.');
    }

    $field_access = $access_handler->fieldAccess('edit', $field_definition, $account, $items, TRUE);
    return $process_result($entity_access->andIf($field_access));
  }

  /**
+27 −5
Original line number Diff line number Diff line
@@ -53,6 +53,13 @@ class MediaLibraryUiBuilder {
   */
  protected $viewsExecutableFactory;

  /**
   * The media library opener resolver.
   *
   * @var \Drupal\media_library\OpenerResolverInterface
   */
  protected $openerResolver;

  /**
   * Constructs a MediaLibraryUiBuilder instance.
   *
@@ -64,12 +71,19 @@ class MediaLibraryUiBuilder {
   *   The views executable factory.
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The currently active request object.
   * @param \Drupal\media_library\OpenerResolverInterface $opener_resolver
   *   The opener resolver.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) {
  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder, OpenerResolverInterface $opener_resolver = NULL) {
    $this->entityTypeManager = $entity_type_manager;
    $this->request = $request_stack->getCurrentRequest();
    $this->viewsExecutableFactory = $views_executable_factory;
    $this->formBuilder = $form_builder;
    if (!$opener_resolver) {
      @trigger_error('The media_library.opener_resolver service must be passed to ' . __METHOD__ . ' and will be required before Drupal 9.0.0.', E_USER_DEPRECATED);
      $opener_resolver = \Drupal::service('media_library.opener_resolver');
    }
    $this->openerResolver = $opener_resolver;
  }

  /**
@@ -159,7 +173,7 @@ protected function buildLibraryContent(MediaLibraryState $state) {
   * Check access to the media library.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   (optional) Run access checks for this account.
   *   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.
@@ -167,10 +181,10 @@ protected function buildLibraryContent(MediaLibraryState $state) {
   * @return \Drupal\Core\Access\AccessResult
   *   The access result.
   */
  public function checkAccess(AccountInterface $account = NULL, MediaLibraryState $state = NULL) {
  public function checkAccess(AccountInterface $account, MediaLibraryState $state = NULL) {
    if (!$state) {
      try {
        MediaLibraryState::fromRequest($this->request);
        $state = MediaLibraryState::fromRequest($this->request);
      }
      catch (BadRequestHttpException $e) {
        return AccessResult::forbidden($e->getMessage());
@@ -189,8 +203,16 @@ public function checkAccess(AccountInterface $account = NULL, MediaLibraryState
      return AccessResult::forbidden('The media library widget display does not exist.')
        ->addCacheableDependency($view);
    }
    return AccessResult::allowedIfHasPermission($account, 'view media')

    // The user must at least be able to view media in order to access the media
    // library.
    $can_view_media = AccessResult::allowedIfHasPermission($account, 'view media')
      ->addCacheableDependency($view);

    // Delegate any further access checking to the opener service nominated by
    // the media library state.
    return $this->openerResolver->get($state)->checkAccess($state, $account)
      ->andIf($can_view_media);
  }

  /**
+9 −1
Original line number Diff line number Diff line
@@ -461,9 +461,17 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
    // This particular media library opener needs some extra metadata for its
    // \Drupal\media_library\MediaLibraryOpenerInterface::getSelectionResponse()
    // to be able to target the element whose 'data-media-library-widget-value'
    // attribute is the same as $field_widget_id.
    // attribute is the same as $field_widget_id. The entity ID, entity type ID,
    // bundle, field name are used for access checking.
    $entity = $items->getEntity();
    $state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_media_type_ids, $selected_type_id, $remaining, [
      'field_widget_id' => $field_widget_id,
      'entity_type_id' => $entity->getEntityTypeId(),
      'bundle' => $entity->bundle(),
      'field_name' => $field_name,
      // The entity ID needs to be a string to ensure that the media library
      // state generates its tamper-proof hash in a consistent way.
      'entity_id' => (string) $entity->id(),
    ]);

    // Add a button that will load the Media library in a modal using AJAX.
+18 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Contains hook implementations for the media_library_test module.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Implements hook_entity_field_access().
 */
function media_library_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
  return AccessResult::forbiddenIf($field_definition->getName() === 'field_media_no_access', 'Field access denied by test module');
}
Loading