annotate 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
rev   line source
Chris@5 1 <?php
Chris@5 2
Chris@5 3 namespace Drupal\media_library;
Chris@5 4
Chris@5 5 use Drupal\Component\Utility\Crypt;
Chris@5 6 use Drupal\Core\Site\Settings;
Chris@5 7 use Symfony\Component\HttpFoundation\ParameterBag;
Chris@5 8 use Symfony\Component\HttpFoundation\Request;
Chris@5 9 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
Chris@5 10
Chris@5 11 /**
Chris@5 12 * A value object for the media library state.
Chris@5 13 *
Chris@5 14 * When the media library is opened it needs several parameters to work
Chris@5 15 * properly. The parameters are retrieved from the MediaLibraryState value
Chris@5 16 * object. Since the parameters are passed via the URL, the value object is
Chris@5 17 * extended from ParameterBag. This also allows an opener to add extra
Chris@5 18 * parameters if needed. The following parameters are needed to open the media
Chris@5 19 * library:
Chris@5 20 * - media_library_opener_id: The opener ID is used to describe the "thing" that
Chris@5 21 * opened the media library. Most of the time this is going to be a form
Chris@5 22 * field.
Chris@5 23 * - media_library_allowed_types: The media types available in the library can
Chris@5 24 * be restricted to a list of allowed types. This should be an array of media
Chris@5 25 * type IDs.
Chris@5 26 * - media_library_selected_type: The media library contains tabs to navigate
Chris@5 27 * between the different media types. The selected type contains the ID of the
Chris@5 28 * media type whose tab that should be opened.
Chris@5 29 * - media_library_remaining: When the opener wants to limit the amount of media
Chris@5 30 * items that can be selected, it can pass the number of remaining slots. When
Chris@5 31 * the number of remaining slots is a negative number, an unlimited amount of
Chris@5 32 * items can be selected.
Chris@5 33 *
Chris@5 34 * @internal
Chris@5 35 * Media Library is an experimental module and its internal code may be
Chris@5 36 * subject to change in minor releases. External code should not instantiate
Chris@5 37 * or extend this class.
Chris@5 38 */
Chris@5 39 class MediaLibraryState extends ParameterBag {
Chris@5 40
Chris@5 41 /**
Chris@5 42 * {@inheritdoc}
Chris@5 43 */
Chris@5 44 public function __construct(array $parameters = []) {
Chris@5 45 $this->validateParameters($parameters['media_library_opener_id'], $parameters['media_library_allowed_types'], $parameters['media_library_selected_type'], $parameters['media_library_remaining']);
Chris@5 46 parent::__construct($parameters);
Chris@5 47 // Add a hash to the state parameters.
Chris@5 48 $this->set('hash', $this->getHash());
Chris@5 49 }
Chris@5 50
Chris@5 51 /**
Chris@5 52 * Creates a new MediaLibraryState object.
Chris@5 53 *
Chris@5 54 * @param string $opener_id
Chris@5 55 * The opener ID.
Chris@5 56 * @param string[] $allowed_media_type_ids
Chris@5 57 * The allowed media type IDs.
Chris@5 58 * @param string $selected_type_id
Chris@5 59 * The selected media type ID.
Chris@5 60 * @param int $remaining_slots
Chris@5 61 * The number of remaining items the user is allowed to select or add in the
Chris@5 62 * library.
Chris@5 63 *
Chris@5 64 * @return \Drupal\media_library\MediaLibraryState
Chris@5 65 * A state object.
Chris@5 66 */
Chris@5 67 public static function create($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) {
Chris@5 68 $state = new static([
Chris@5 69 'media_library_opener_id' => $opener_id,
Chris@5 70 'media_library_allowed_types' => $allowed_media_type_ids,
Chris@5 71 'media_library_selected_type' => $selected_type_id,
Chris@5 72 'media_library_remaining' => $remaining_slots,
Chris@5 73 ]);
Chris@5 74 return $state;
Chris@5 75 }
Chris@5 76
Chris@5 77 /**
Chris@5 78 * Get the media library state from a request.
Chris@5 79 *
Chris@5 80 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@5 81 * The request.
Chris@5 82 *
Chris@5 83 * @return \Drupal\media_library\MediaLibraryState
Chris@5 84 * A state object.
Chris@5 85 *
Chris@5 86 * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
Chris@5 87 * Thrown when the hash query parameter is invalid.
Chris@5 88 */
Chris@5 89 public static function fromRequest(Request $request) {
Chris@5 90 $query = $request->query;
Chris@5 91
Chris@5 92 // Create a MediaLibraryState object through the create method to make sure
Chris@5 93 // all validation runs.
Chris@5 94 $state = static::create(
Chris@5 95 $query->get('media_library_opener_id'),
Chris@5 96 $query->get('media_library_allowed_types'),
Chris@5 97 $query->get('media_library_selected_type'),
Chris@5 98 $query->get('media_library_remaining')
Chris@5 99 );
Chris@5 100
Chris@5 101 // The request parameters need to contain a valid hash to prevent a
Chris@5 102 // malicious user modifying the query string to attempt to access
Chris@5 103 // inaccessible information.
Chris@5 104 if (!$state->isValidHash($query->get('hash'))) {
Chris@5 105 throw new BadRequestHttpException("Invalid media library parameters specified.");
Chris@5 106 }
Chris@5 107
Chris@5 108 // Once we have validated the required parameters, we restore the parameters
Chris@5 109 // from the request since there might be additional values.
Chris@5 110 $state->replace($query->all());
Chris@5 111 return $state;
Chris@5 112 }
Chris@5 113
Chris@5 114 /**
Chris@5 115 * Validate the required parameters for a new MediaLibraryState object.
Chris@5 116 *
Chris@5 117 * @param string $opener_id
Chris@5 118 * The opener ID.
Chris@5 119 * @param string[] $allowed_media_type_ids
Chris@5 120 * The allowed media type IDs.
Chris@5 121 * @param string $selected_type_id
Chris@5 122 * The selected media type ID.
Chris@5 123 * @param int $remaining_slots
Chris@5 124 * The number of remaining items the user is allowed to select or add in the
Chris@5 125 * library.
Chris@5 126 *
Chris@5 127 * @throws \InvalidArgumentException
Chris@5 128 * If one of the passed arguments is missing or does not pass the
Chris@5 129 * validation.
Chris@5 130 */
Chris@5 131 protected function validateParameters($opener_id, array $allowed_media_type_ids, $selected_type_id, $remaining_slots) {
Chris@5 132 // The opener ID must be a non-empty string.
Chris@5 133 if (!is_string($opener_id) || empty(trim($opener_id))) {
Chris@5 134 throw new \InvalidArgumentException('The opener ID parameter is required and must be a string.');
Chris@5 135 }
Chris@5 136
Chris@5 137 // The allowed media type IDs must be an array of non-empty strings.
Chris@5 138 if (empty($allowed_media_type_ids) || !is_array($allowed_media_type_ids)) {
Chris@5 139 throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.');
Chris@5 140 }
Chris@5 141 foreach ($allowed_media_type_ids as $allowed_media_type_id) {
Chris@5 142 if (!is_string($allowed_media_type_id) || empty(trim($allowed_media_type_id))) {
Chris@5 143 throw new \InvalidArgumentException('The allowed types parameter is required and must be an array of strings.');
Chris@5 144 }
Chris@5 145 }
Chris@5 146
Chris@5 147 // The selected type ID must be a non-empty string.
Chris@5 148 if (!is_string($selected_type_id) || empty(trim($selected_type_id))) {
Chris@5 149 throw new \InvalidArgumentException('The selected type parameter is required and must be a string.');
Chris@5 150 }
Chris@5 151 // The selected type ID must be present in the list of allowed types.
Chris@5 152 if (!in_array($selected_type_id, $allowed_media_type_ids, TRUE)) {
Chris@5 153 throw new \InvalidArgumentException('The selected type parameter must be present in the list of allowed types.');
Chris@5 154 }
Chris@5 155
Chris@5 156 // The remaining slots must be numeric.
Chris@5 157 if (!is_numeric($remaining_slots)) {
Chris@5 158 throw new \InvalidArgumentException('The remaining slots parameter is required and must be numeric.');
Chris@5 159 }
Chris@5 160 }
Chris@5 161
Chris@5 162 /**
Chris@5 163 * Get the hash for the state object.
Chris@5 164 *
Chris@5 165 * @return string
Chris@5 166 * The hashed parameters.
Chris@5 167 */
Chris@5 168 public function getHash() {
Chris@5 169 // Create a hash from the required state parameters.
Chris@5 170 $hash = implode(':', [
Chris@5 171 $this->getOpenerId(),
Chris@5 172 implode(':', $this->getAllowedTypeIds()),
Chris@5 173 $this->getSelectedTypeId(),
Chris@5 174 $this->getAvailableSlots(),
Chris@5 175 ]);
Chris@5 176
Chris@5 177 return Crypt::hmacBase64($hash, \Drupal::service('private_key')->get() . Settings::getHashSalt());
Chris@5 178 }
Chris@5 179
Chris@5 180 /**
Chris@5 181 * Validate a hash for the state object.
Chris@5 182 *
Chris@5 183 * @param string $hash
Chris@5 184 * The hash to validate.
Chris@5 185 *
Chris@5 186 * @return string
Chris@5 187 * The hashed parameters.
Chris@5 188 */
Chris@5 189 public function isValidHash($hash) {
Chris@5 190 return Crypt::hashEquals($this->getHash(), $hash);
Chris@5 191 }
Chris@5 192
Chris@5 193 /**
Chris@5 194 * Returns the ID of the opener of the media library.
Chris@5 195 *
Chris@5 196 * @return string
Chris@5 197 * The opener ID.
Chris@5 198 */
Chris@5 199 public function getOpenerId() {
Chris@5 200 return $this->get('media_library_opener_id');
Chris@5 201 }
Chris@5 202
Chris@5 203 /**
Chris@5 204 * Returns the media type IDs which can be selected.
Chris@5 205 *
Chris@5 206 * @return string[]
Chris@5 207 * The media type IDs.
Chris@5 208 */
Chris@5 209 public function getAllowedTypeIds() {
Chris@5 210 return $this->get('media_library_allowed_types');
Chris@5 211 }
Chris@5 212
Chris@5 213 /**
Chris@5 214 * Returns the selected media type.
Chris@5 215 *
Chris@5 216 * @return string
Chris@5 217 * The selected media type.
Chris@5 218 */
Chris@5 219 public function getSelectedTypeId() {
Chris@5 220 return $this->get('media_library_selected_type');
Chris@5 221 }
Chris@5 222
Chris@5 223 /**
Chris@5 224 * Determines if additional media items can be selected.
Chris@5 225 *
Chris@5 226 * @return bool
Chris@5 227 * TRUE if additional items can be selected, otherwise FALSE.
Chris@5 228 */
Chris@5 229 public function hasSlotsAvailable() {
Chris@5 230 return $this->getAvailableSlots() !== 0;
Chris@5 231 }
Chris@5 232
Chris@5 233 /**
Chris@5 234 * Returns the number of additional media items that can be selected.
Chris@5 235 *
Chris@5 236 * When the value is not available in the URL the default is 0. When a
Chris@5 237 * negative integer is passed, an unlimited amount of media items can be
Chris@5 238 * selected.
Chris@5 239 *
Chris@5 240 * @return int
Chris@5 241 * The number of additional media items that can be selected.
Chris@5 242 */
Chris@5 243 public function getAvailableSlots() {
Chris@5 244 return $this->getInt('media_library_remaining');
Chris@5 245 }
Chris@5 246
Chris@5 247 }