Chris@18: validateParameters($parameters['media_library_opener_id'], $parameters['media_library_allowed_types'], $parameters['media_library_selected_type'], $parameters['media_library_remaining']); Chris@18: parent::__construct($parameters); Chris@18: // Add a hash to the state parameters. Chris@18: $this->set('hash', $this->getHash()); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Creates a new MediaLibraryState object. Chris@18: * Chris@18: * @param string $opener_id Chris@18: * The opener ID. Chris@18: * @param string[] $allowed_media_type_ids Chris@18: * The allowed media type IDs. Chris@18: * @param string $selected_type_id Chris@18: * The selected media type ID. Chris@18: * @param int $remaining_slots Chris@18: * The number of remaining items the user is allowed to select or add in the Chris@18: * library. Chris@18: * Chris@18: * @return \Drupal\media_library\MediaLibraryState Chris@18: * A state object. Chris@18: */ Chris@18: public static function create($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) { Chris@18: $state = new static([ Chris@18: 'media_library_opener_id' => $opener_id, Chris@18: 'media_library_allowed_types' => $allowed_media_type_ids, Chris@18: 'media_library_selected_type' => $selected_type_id, Chris@18: 'media_library_remaining' => $remaining_slots, Chris@18: ]); Chris@18: return $state; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Get the media library state from a request. Chris@18: * Chris@18: * @param \Symfony\Component\HttpFoundation\Request $request Chris@18: * The request. Chris@18: * Chris@18: * @return \Drupal\media_library\MediaLibraryState Chris@18: * A state object. Chris@18: * Chris@18: * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException Chris@18: * Thrown when the hash query parameter is invalid. Chris@18: */ Chris@18: public static function fromRequest(Request $request) { Chris@18: $query = $request->query; Chris@18: Chris@18: // Create a MediaLibraryState object through the create method to make sure Chris@18: // all validation runs. Chris@18: $state = static::create( Chris@18: $query->get('media_library_opener_id'), Chris@18: $query->get('media_library_allowed_types'), Chris@18: $query->get('media_library_selected_type'), Chris@18: $query->get('media_library_remaining') Chris@18: ); Chris@18: Chris@18: // The request parameters need to contain a valid hash to prevent a Chris@18: // malicious user modifying the query string to attempt to access Chris@18: // inaccessible information. Chris@18: if (!$state->isValidHash($query->get('hash'))) { Chris@18: throw new BadRequestHttpException("Invalid media library parameters specified."); Chris@18: } Chris@18: Chris@18: // Once we have validated the required parameters, we restore the parameters Chris@18: // from the request since there might be additional values. Chris@18: $state->replace($query->all()); Chris@18: return $state; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Validate the required parameters for a new MediaLibraryState object. Chris@18: * Chris@18: * @param string $opener_id Chris@18: * The opener ID. Chris@18: * @param string[] $allowed_media_type_ids Chris@18: * The allowed media type IDs. Chris@18: * @param string $selected_type_id Chris@18: * The selected media type ID. Chris@18: * @param int $remaining_slots Chris@18: * The number of remaining items the user is allowed to select or add in the Chris@18: * library. Chris@18: * Chris@18: * @throws \InvalidArgumentException Chris@18: * If one of the passed arguments is missing or does not pass the Chris@18: * validation. Chris@18: */ Chris@18: protected function validateParameters($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) { Chris@18: // The opener ID must be a non-empty string. Chris@18: if (!is_string($opener_id) || empty(trim($opener_id))) { Chris@18: throw new \InvalidArgumentException('The opener ID parameter is required and must be a string.'); Chris@18: } Chris@18: Chris@18: // The allowed media type IDs must be an array of non-empty strings. Chris@18: if (empty($allowed_media_type_ids) || !is_array($allowed_media_type_ids)) { Chris@18: throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.'); Chris@18: } Chris@18: foreach ($allowed_media_type_ids as $allowed_media_type_id) { Chris@18: if (!is_string($allowed_media_type_id) || empty(trim($allowed_media_type_id))) { Chris@18: throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.'); Chris@18: } Chris@18: } Chris@18: Chris@18: // The selected type ID must be a non-empty string. Chris@18: if (!is_string($selected_type_id) || empty(trim($selected_type_id))) { Chris@18: throw new \InvalidArgumentException('The selected type parameter is required and must be a string.'); Chris@18: } Chris@18: // The selected type ID must be present in the list of allowed types. Chris@18: if (!in_array($selected_type_id, $allowed_media_type_ids, TRUE)) { Chris@18: throw new \InvalidArgumentException('The selected type parameter must be present in the list of allowed types.'); Chris@18: } Chris@18: Chris@18: // The remaining slots must be numeric. Chris@18: if (!is_numeric($remaining_slots)) { Chris@18: throw new \InvalidArgumentException('The remaining slots parameter is required and must be numeric.'); Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Get the hash for the state object. Chris@18: * Chris@18: * @return string Chris@18: * The hashed parameters. Chris@18: */ Chris@18: public function getHash() { Chris@18: // Create a hash from the required state parameters. Chris@18: $hash = implode(':', [ Chris@18: $this->getOpenerId(), Chris@18: implode(':', $this->getAllowedTypeIds()), Chris@18: $this->getSelectedTypeId(), Chris@18: $this->getAvailableSlots(), Chris@18: ]); Chris@18: Chris@18: return Crypt::hmacBase64($hash, \Drupal::service('private_key')->get() . Settings::getHashSalt()); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Validate a hash for the state object. Chris@18: * Chris@18: * @param string $hash Chris@18: * The hash to validate. Chris@18: * Chris@18: * @return string Chris@18: * The hashed parameters. Chris@18: */ Chris@18: public function isValidHash($hash) { Chris@18: return Crypt::hashEquals($this->getHash(), $hash); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Returns the ID of the opener of the media library. Chris@18: * Chris@18: * @return string Chris@18: * The opener ID. Chris@18: */ Chris@18: public function getOpenerId() { Chris@18: return $this->get('media_library_opener_id'); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Returns the media type IDs which can be selected. Chris@18: * Chris@18: * @return string[] Chris@18: * The media type IDs. Chris@18: */ Chris@18: public function getAllowedTypeIds() { Chris@18: return $this->get('media_library_allowed_types'); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Returns the selected media type. Chris@18: * Chris@18: * @return string Chris@18: * The selected media type. Chris@18: */ Chris@18: public function getSelectedTypeId() { Chris@18: return $this->get('media_library_selected_type'); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Determines if additional media items can be selected. Chris@18: * Chris@18: * @return bool Chris@18: * TRUE if additional items can be selected, otherwise FALSE. Chris@18: */ Chris@18: public function hasSlotsAvailable() { Chris@18: return $this->getAvailableSlots() !== 0; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Returns the number of additional media items that can be selected. Chris@18: * Chris@18: * When the value is not available in the URL the default is 0. When a Chris@18: * negative integer is passed, an unlimited amount of media items can be Chris@18: * selected. Chris@18: * Chris@18: * @return int Chris@18: * The number of additional media items that can be selected. Chris@18: */ Chris@18: public function getAvailableSlots() { Chris@18: return $this->getInt('media_library_remaining'); Chris@18: } Chris@18: Chris@18: }