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