diff core/modules/media_library/src/MediaLibraryState.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/MediaLibraryState.php	Thu May 09 15:34:47 2019 +0100
@@ -0,0 +1,247 @@
+<?php
+
+namespace Drupal\media_library;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Site\Settings;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+
+/**
+ * A value object for the media library state.
+ *
+ * When the media library is opened it needs several parameters to work
+ * properly. The parameters are retrieved from the MediaLibraryState value
+ * object. Since the parameters are passed via the URL, the value object is
+ * extended from ParameterBag. This also allows an opener to add extra
+ * parameters if needed. The following parameters are needed to open the media
+ * library:
+ * - media_library_opener_id: The opener ID is used to describe the "thing" that
+ *   opened the media library. Most of the time this is going to be a form
+ *   field.
+ * - media_library_allowed_types: The media types available in the library can
+ *   be restricted to a list of allowed types. This should be an array of media
+ *   type IDs.
+ * - media_library_selected_type: The media library contains tabs to navigate
+ *   between the different media types. The selected type contains the ID of the
+ *   media type whose tab that should be opened.
+ * - media_library_remaining: When the opener wants to limit the amount of media
+ *   items that can be selected, it can pass the number of remaining slots. When
+ *   the number of remaining slots is a negative number, an unlimited amount of
+ *   items can be selected.
+ *
+ * @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 MediaLibraryState extends ParameterBag {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $parameters = []) {
+    $this->validateParameters($parameters['media_library_opener_id'], $parameters['media_library_allowed_types'], $parameters['media_library_selected_type'], $parameters['media_library_remaining']);
+    parent::__construct($parameters);
+    // Add a hash to the state parameters.
+    $this->set('hash', $this->getHash());
+  }
+
+  /**
+   * Creates a new MediaLibraryState object.
+   *
+   * @param string $opener_id
+   *   The opener ID.
+   * @param string[] $allowed_media_type_ids
+   *   The allowed media type IDs.
+   * @param string $selected_type_id
+   *   The selected media type ID.
+   * @param int $remaining_slots
+   *   The number of remaining items the user is allowed to select or add in the
+   *   library.
+   *
+   * @return \Drupal\media_library\MediaLibraryState
+   *   A state object.
+   */
+  public static function create($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) {
+    $state = new static([
+      'media_library_opener_id' => $opener_id,
+      'media_library_allowed_types' => $allowed_media_type_ids,
+      'media_library_selected_type' => $selected_type_id,
+      'media_library_remaining' => $remaining_slots,
+    ]);
+    return $state;
+  }
+
+  /**
+   * Get the media library state from a request.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return \Drupal\media_library\MediaLibraryState
+   *   A state object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   *   Thrown when the hash query parameter is invalid.
+   */
+  public static function fromRequest(Request $request) {
+    $query = $request->query;
+
+    // Create a MediaLibraryState object through the create method to make sure
+    // all validation runs.
+    $state = static::create(
+      $query->get('media_library_opener_id'),
+      $query->get('media_library_allowed_types'),
+      $query->get('media_library_selected_type'),
+      $query->get('media_library_remaining')
+    );
+
+    // The request parameters need to contain a valid hash to prevent a
+    // malicious user modifying the query string to attempt to access
+    // inaccessible information.
+    if (!$state->isValidHash($query->get('hash'))) {
+      throw new BadRequestHttpException("Invalid media library parameters specified.");
+    }
+
+    // Once we have validated the required parameters, we restore the parameters
+    // from the request since there might be additional values.
+    $state->replace($query->all());
+    return $state;
+  }
+
+  /**
+   * Validate the required parameters for a new MediaLibraryState object.
+   *
+   * @param string $opener_id
+   *   The opener ID.
+   * @param string[] $allowed_media_type_ids
+   *   The allowed media type IDs.
+   * @param string $selected_type_id
+   *   The selected media type ID.
+   * @param int $remaining_slots
+   *   The number of remaining items the user is allowed to select or add in the
+   *   library.
+   *
+   * @throws \InvalidArgumentException
+   *   If one of the passed arguments is missing or does not pass the
+   *   validation.
+   */
+  protected function validateParameters($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) {
+    // The opener ID must be a non-empty string.
+    if (!is_string($opener_id) || empty(trim($opener_id))) {
+      throw new \InvalidArgumentException('The opener ID parameter is required and must be a string.');
+    }
+
+    // The allowed media type IDs must be an array of non-empty strings.
+    if (empty($allowed_media_type_ids) || !is_array($allowed_media_type_ids)) {
+      throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.');
+    }
+    foreach ($allowed_media_type_ids as $allowed_media_type_id) {
+      if (!is_string($allowed_media_type_id) || empty(trim($allowed_media_type_id))) {
+        throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.');
+      }
+    }
+
+    // The selected type ID must be a non-empty string.
+    if (!is_string($selected_type_id) || empty(trim($selected_type_id))) {
+      throw new \InvalidArgumentException('The selected type parameter is required and must be a string.');
+    }
+    // The selected type ID must be present in the list of allowed types.
+    if (!in_array($selected_type_id, $allowed_media_type_ids, TRUE)) {
+      throw new \InvalidArgumentException('The selected type parameter must be present in the list of allowed types.');
+    }
+
+    // The remaining slots must be numeric.
+    if (!is_numeric($remaining_slots)) {
+      throw new \InvalidArgumentException('The remaining slots parameter is required and must be numeric.');
+    }
+  }
+
+  /**
+   * Get the hash for the state object.
+   *
+   * @return string
+   *   The hashed parameters.
+   */
+  public function getHash() {
+    // Create a hash from the required state parameters.
+    $hash = implode(':', [
+      $this->getOpenerId(),
+      implode(':', $this->getAllowedTypeIds()),
+      $this->getSelectedTypeId(),
+      $this->getAvailableSlots(),
+    ]);
+
+    return Crypt::hmacBase64($hash, \Drupal::service('private_key')->get() . Settings::getHashSalt());
+  }
+
+  /**
+   * Validate a hash for the state object.
+   *
+   * @param string $hash
+   *   The hash to validate.
+   *
+   * @return string
+   *   The hashed parameters.
+   */
+  public function isValidHash($hash) {
+    return Crypt::hashEquals($this->getHash(), $hash);
+  }
+
+  /**
+   * Returns the ID of the opener of the media library.
+   *
+   * @return string
+   *   The opener ID.
+   */
+  public function getOpenerId() {
+    return $this->get('media_library_opener_id');
+  }
+
+  /**
+   * Returns the media type IDs which can be selected.
+   *
+   * @return string[]
+   *   The media type IDs.
+   */
+  public function getAllowedTypeIds() {
+    return $this->get('media_library_allowed_types');
+  }
+
+  /**
+   * Returns the selected media type.
+   *
+   * @return string
+   *   The selected media type.
+   */
+  public function getSelectedTypeId() {
+    return $this->get('media_library_selected_type');
+  }
+
+  /**
+   * Determines if additional media items can be selected.
+   *
+   * @return bool
+   *   TRUE if additional items can be selected, otherwise FALSE.
+   */
+  public function hasSlotsAvailable() {
+    return $this->getAvailableSlots() !== 0;
+  }
+
+  /**
+   * Returns the number of additional media items that can be selected.
+   *
+   * When the value is not available in the URL the default is 0. When a
+   * negative integer is passed, an unlimited amount of media items can be
+   * selected.
+   *
+   * @return int
+   *   The number of additional media items that can be selected.
+   */
+  public function getAvailableSlots() {
+    return $this->getInt('media_library_remaining');
+  }
+
+}