Mercurial > hg > cmmr2012-drupal-site
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 } |