comparison core/modules/media_library/src/MediaLibraryUiBuilder.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\Core\Access\AccessResult;
6 use Drupal\Core\Form\FormBuilderInterface;
7 use Drupal\Core\Form\FormState;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\Core\StringTranslation\StringTranslationTrait;
11 use Drupal\Core\Url;
12 use Drupal\views\ViewExecutableFactory;
13 use Symfony\Component\HttpFoundation\RequestStack;
14 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
15
16 /**
17 * Service which builds the media library.
18 *
19 * @internal
20 * Media Library is an experimental module and its internal code may be
21 * subject to change in minor releases. External code should not instantiate
22 * or extend this class.
23 */
24 class MediaLibraryUiBuilder {
25
26 use StringTranslationTrait;
27
28 /**
29 * The form builder.
30 *
31 * @var \Drupal\Core\Form\FormBuilderInterface
32 */
33 protected $formBuilder;
34
35 /**
36 * The entity type manager.
37 *
38 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
39 */
40 protected $entityTypeManager;
41
42 /**
43 * The currently active request object.
44 *
45 * @var \Symfony\Component\HttpFoundation\Request
46 */
47 protected $request;
48
49 /**
50 * The views executable factory.
51 *
52 * @var \Drupal\views\ViewExecutableFactory
53 */
54 protected $viewsExecutableFactory;
55
56 /**
57 * Constructs a MediaLibraryUiBuilder instance.
58 *
59 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
60 * The entity type manager.
61 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
62 * The request stack.
63 * @param \Drupal\views\ViewExecutableFactory $views_executable_factory
64 * The views executable factory.
65 * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
66 * The currently active request object.
67 */
68 public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) {
69 $this->entityTypeManager = $entity_type_manager;
70 $this->request = $request_stack->getCurrentRequest();
71 $this->viewsExecutableFactory = $views_executable_factory;
72 $this->formBuilder = $form_builder;
73 }
74
75 /**
76 * Get media library dialog options.
77 *
78 * @return array
79 * The media library dialog options.
80 */
81 public static function dialogOptions() {
82 return [
83 'dialogClass' => 'media-library-widget-modal',
84 'title' => t('Add or select media'),
85 'height' => '75%',
86 'width' => '75%',
87 ];
88 }
89
90 /**
91 * Build the media library UI.
92 *
93 * @param \Drupal\media_library\MediaLibraryState $state
94 * (optional) The current state of the media library, derived from the
95 * current request.
96 *
97 * @return array
98 * The render array for the media library.
99 */
100 public function buildUi(MediaLibraryState $state = NULL) {
101 if (!$state) {
102 $state = MediaLibraryState::fromRequest($this->request);
103 }
104 // When navigating to a media type through the vertical tabs, we only want
105 // to load the changed library content. This is not only more efficient, but
106 // also provides a more accessible user experience for screen readers.
107 if ($state->get('media_library_content') === '1') {
108 return $this->buildLibraryContent($state);
109 }
110 else {
111 return [
112 '#type' => 'html_tag',
113 '#tag' => 'div',
114 '#attributes' => [
115 'id' => 'media-library-wrapper',
116 'class' => ['media-library-wrapper'],
117 ],
118 'menu' => $this->buildMediaTypeMenu($state),
119 'content' => $this->buildLibraryContent($state),
120 // Attach the JavaScript for the media library UI. The number of
121 // available slots needs to be added to make sure users can't select
122 // more items than allowed.
123 '#attached' => [
124 'library' => ['media_library/ui'],
125 'drupalSettings' => [
126 'media_library' => [
127 'selection_remaining' => $state->getAvailableSlots(),
128 ],
129 ],
130 ],
131 ];
132 }
133 }
134
135 /**
136 * Build the media library content area.
137 *
138 * @param \Drupal\media_library\MediaLibraryState $state
139 * The current state of the media library, derived from the current request.
140 *
141 * @return array
142 * The render array for the media library.
143 */
144 protected function buildLibraryContent(MediaLibraryState $state) {
145 return [
146 '#type' => 'html_tag',
147 '#tag' => 'div',
148 '#attributes' => [
149 'id' => 'media-library-content',
150 'class' => ['media-library-content'],
151 'tabindex' => -1,
152 ],
153 'form' => $this->buildMediaTypeAddForm($state),
154 'view' => $this->buildMediaLibraryView($state),
155 ];
156 }
157
158 /**
159 * Check access to the media library.
160 *
161 * @param \Drupal\Core\Session\AccountInterface $account
162 * (optional) Run access checks for this account.
163 * @param \Drupal\media_library\MediaLibraryState $state
164 * (optional) The current state of the media library, derived from the
165 * current request.
166 *
167 * @return \Drupal\Core\Access\AccessResult
168 * The access result.
169 */
170 public function checkAccess(AccountInterface $account = NULL, MediaLibraryState $state = NULL) {
171 if (!$state) {
172 try {
173 MediaLibraryState::fromRequest($this->request);
174 }
175 catch (BadRequestHttpException $e) {
176 return AccessResult::forbidden($e->getMessage());
177 }
178 catch (\InvalidArgumentException $e) {
179 return AccessResult::forbidden($e->getMessage());
180 }
181 }
182 // Deny access if the view or display are removed.
183 $view = $this->entityTypeManager->getStorage('view')->load('media_library');
184 if (!$view) {
185 return AccessResult::forbidden('The media library view does not exist.')
186 ->setCacheMaxAge(0);
187 }
188 if (!$view->getDisplay('widget')) {
189 return AccessResult::forbidden('The media library widget display does not exist.')
190 ->addCacheableDependency($view);
191 }
192 return AccessResult::allowedIfHasPermission($account, 'view media')
193 ->addCacheableDependency($view);
194 }
195
196 /**
197 * Get the media type menu for the media library.
198 *
199 * @param \Drupal\media_library\MediaLibraryState $state
200 * The current state of the media library, derived from the current request.
201 *
202 * @return array
203 * The render array for the media type menu.
204 */
205 protected function buildMediaTypeMenu(MediaLibraryState $state) {
206 // Add the menu for each type if we have more than 1 media type enabled for
207 // the field.
208 $allowed_type_ids = $state->getAllowedTypeIds();
209 if (count($allowed_type_ids) <= 1) {
210 return [];
211 }
212
213 // @todo: Add a class to the li element.
214 // https://www.drupal.org/project/drupal/issues/3029227
215 $menu = [
216 '#theme' => 'links',
217 '#links' => [],
218 '#attributes' => [
219 'class' => ['media-library-menu', 'js-media-library-menu'],
220 ],
221 ];
222
223 $allowed_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple($allowed_type_ids);
224
225 $selected_type_id = $state->getSelectedTypeId();
226 foreach ($allowed_types as $allowed_type_id => $allowed_type) {
227 $link_state = MediaLibraryState::create($state->getOpenerId(), $state->getAllowedTypeIds(), $allowed_type_id, $state->getAvailableSlots());
228 // Add the 'media_library_content' parameter so the response will contain
229 // only the updated content for the tab.
230 // @see self::buildUi()
231 $link_state->set('media_library_content', 1);
232
233 $title = $allowed_type->label();
234 if ($allowed_type_id === $selected_type_id) {
235 $title = [
236 '#markup' => $this->t('@title<span class="active-tab visually-hidden"> (active tab)</span>', ['@title' => $title]),
237 ];
238 }
239
240 $menu['#links']['media-library-menu-' . $allowed_type_id] = [
241 'title' => $title,
242 'url' => Url::fromRoute('media_library.ui', [], [
243 'query' => $link_state->all(),
244 ]),
245 'attributes' => [
246 'class' => ['media-library-menu__link'],
247 ],
248 ];
249 }
250
251 // Set the active menu item.
252 $menu['#links']['media-library-menu-' . $selected_type_id]['attributes']['class'][] = 'active';
253
254 return $menu;
255 }
256
257 /**
258 * Get the add form for the selected media type.
259 *
260 * @param \Drupal\media_library\MediaLibraryState $state
261 * The current state of the media library, derived from the current request.
262 *
263 * @return array
264 * The render array for the media type add form.
265 */
266 protected function buildMediaTypeAddForm(MediaLibraryState $state) {
267 $selected_type_id = $state->getSelectedTypeId();
268
269 if (!$this->entityTypeManager->getAccessControlHandler('media')->createAccess($selected_type_id)) {
270 return [];
271 }
272
273 $selected_type = $this->entityTypeManager->getStorage('media_type')->load($selected_type_id);
274 $plugin_definition = $selected_type->getSource()->getPluginDefinition();
275
276 if (empty($plugin_definition['forms']['media_library_add'])) {
277 return [];
278 }
279
280 // After the form to add new media is submitted, we need to rebuild the
281 // media library with a new instance of the media add form. The form API
282 // allows us to do that by forcing empty user input.
283 // @see \Drupal\Core\Form\FormBuilder::doBuildForm()
284 $form_state = new FormState();
285 if ($state->get('_media_library_form_rebuild')) {
286 $form_state->setUserInput([]);
287 $state->remove('_media_library_form_rebuild');
288 }
289 $form_state->set('media_library_state', $state);
290 return $this->formBuilder->buildForm($plugin_definition['forms']['media_library_add'], $form_state);
291 }
292
293 /**
294 * Get the media library view.
295 *
296 * @param \Drupal\media_library\MediaLibraryState $state
297 * The current state of the media library, derived from the current request.
298 *
299 * @return array
300 * The render array for the media library view.
301 */
302 protected function buildMediaLibraryView(MediaLibraryState $state) {
303 // @todo Make the view configurable in
304 // https://www.drupal.org/project/drupal/issues/2971209
305 $view = $this->entityTypeManager->getStorage('view')->load('media_library');
306 $view_executable = $this->viewsExecutableFactory->get($view);
307 $display_id = 'widget';
308
309 // Make sure the state parameters are set in the request so the view can
310 // pass the parameters along in the pager, filters etc.
311 $view_request = $view_executable->getRequest();
312 $view_request->query->add($state->all());
313 $view_executable->setRequest($view_request);
314
315 $args = [$state->getSelectedTypeId()];
316
317 // Make sure the state parameters are set in the request so the view can
318 // pass the parameters along in the pager, filters etc.
319 $request = $view_executable->getRequest();
320 $request->query->add($state->all());
321 $view_executable->setRequest($request);
322
323 $view_executable->setDisplay($display_id);
324 $view_executable->preExecute($args);
325 $view_executable->execute($display_id);
326
327 return $view_executable->buildRenderable($display_id, $args, FALSE);
328 }
329
330 }