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