7766af2e567d63be3dd65acd453ecaf0cd21d6ba
[yaffs-website] / web / core / modules / media / src / MediaTypeForm.php
1 <?php
2
3 namespace Drupal\media;
4
5 use Drupal\Core\Ajax\AjaxResponse;
6 use Drupal\Core\Ajax\ReplaceCommand;
7 use Drupal\Core\Entity\EntityFieldManagerInterface;
8 use Drupal\Core\Entity\EntityForm;
9 use Drupal\Core\Field\BaseFieldDefinition;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Form\SubformState;
12 use Drupal\language\Entity\ContentLanguageSettings;
13 use Drupal\media\Entity\MediaType;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15
16 /**
17  * Form controller for media type forms.
18  *
19  * @internal
20  */
21 class MediaTypeForm extends EntityForm {
22
23   /**
24    * Media source plugin manager.
25    *
26    * @var \Drupal\media\MediaSourceManager
27    */
28   protected $sourceManager;
29
30   /**
31    * Entity field manager service.
32    *
33    * @var \Drupal\Core\Entity\EntityFieldManagerInterface
34    */
35   protected $entityFieldManager;
36
37   /**
38    * Constructs a new class instance.
39    *
40    * @param \Drupal\media\MediaSourceManager $source_manager
41    *   Media source plugin manager.
42    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
43    *   Entity field manager service.
44    */
45   public function __construct(MediaSourceManager $source_manager, EntityFieldManagerInterface $entity_field_manager) {
46     $this->sourceManager = $source_manager;
47     $this->entityFieldManager = $entity_field_manager;
48   }
49
50   /**
51    * {@inheritdoc}
52    */
53   public static function create(ContainerInterface $container) {
54     return new static(
55       $container->get('plugin.manager.media.source'),
56       $container->get('entity_field.manager')
57     );
58   }
59
60   /**
61    * Ajax callback triggered by the type provider select element.
62    */
63   public function ajaxHandlerData(array $form, FormStateInterface $form_state) {
64     $response = new AjaxResponse();
65     $response->addCommand(new ReplaceCommand('#source-dependent', $form['source_dependent']));
66     return $response;
67   }
68
69   /**
70    * {@inheritdoc}
71    */
72   public function form(array $form, FormStateInterface $form_state) {
73     $form = parent::form($form, $form_state);
74
75     // Source is not set when the entity is initially created.
76     /** @var \Drupal\media\MediaSourceInterface $source */
77     $source = $this->entity->get('source') ? $this->entity->getSource() : NULL;
78
79     if ($this->operation === 'add') {
80       $form['#title'] = $this->t('Add media type');
81     }
82
83     $form['label'] = [
84       '#title' => $this->t('Name'),
85       '#type' => 'textfield',
86       '#default_value' => $this->entity->label(),
87       '#description' => $this->t('The human-readable name of this media type.'),
88       '#required' => TRUE,
89       '#size' => 30,
90     ];
91
92     $form['id'] = [
93       '#type' => 'machine_name',
94       '#default_value' => $this->entity->id(),
95       '#maxlength' => 32,
96       '#disabled' => !$this->entity->isNew(),
97       '#machine_name' => [
98         'exists' => [MediaType::class, 'load'],
99       ],
100       '#description' => $this->t('A unique machine-readable name for this media type.'),
101     ];
102
103     $form['description'] = [
104       '#title' => $this->t('Description'),
105       '#type' => 'textarea',
106       '#default_value' => $this->entity->getDescription(),
107       '#description' => $this->t('Describe this media type. The text will be displayed on the <em>Add new media</em> page.'),
108     ];
109
110     $plugins = $this->sourceManager->getDefinitions();
111     $options = [];
112     foreach ($plugins as $plugin_id => $definition) {
113       $options[$plugin_id] = $definition['label'];
114     }
115
116     $form['source_dependent'] = [
117       '#type' => 'container',
118       '#attributes' => ['id' => 'source-dependent'],
119     ];
120
121     if ($source) {
122       $source_description = $this->t('<em>The media source cannot be changed after the media type is created.</em>');
123     }
124     else {
125       $source_description = $this->t('Media source that is responsible for additional logic related to this media type.');
126     }
127     $form['source_dependent']['source'] = [
128       '#type' => 'select',
129       '#title' => $this->t('Media source'),
130       '#default_value' => $source ? $source->getPluginId() : NULL,
131       '#options' => $options,
132       '#description' => $source_description,
133       '#ajax' => ['callback' => '::ajaxHandlerData'],
134       // Rebuilding the form as part of the AJAX request is a workaround to
135       // enforce machine_name validation.
136       // @todo This was added as part of #2932226 and it should be removed once
137       //   https://www.drupal.org/project/drupal/issues/2557299 solves it in a
138       //   more generic way.
139       '#executes_submit_callback' => TRUE,
140       '#submit' => [[static::class, 'rebuildSubmit']],
141       '#required' => TRUE,
142       // Once the media type is created, its source plugin cannot be changed
143       // anymore.
144       '#disabled' => !empty($source),
145     ];
146
147     if (!$source) {
148       $form['type']['#empty_option'] = $this->t('- Select media source -');
149     }
150
151     if ($source) {
152       // Media source plugin configuration.
153       $form['source_dependent']['source_configuration'] = [
154         '#type' => 'fieldset',
155         '#title' => $this->t('Media source configuration'),
156         '#tree' => TRUE,
157       ];
158
159       $form['source_dependent']['source_configuration'] = $source->buildConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
160     }
161
162     // Field mapping configuration.
163     $form['source_dependent']['field_map'] = [
164       '#type' => 'fieldset',
165       '#title' => $this->t('Field mapping'),
166       '#tree' => TRUE,
167       'description' => [
168         '#markup' => '<p>' . $this->t('Media sources can provide metadata fields such as title, caption, size information, credits, etc. Media can automatically save this metadata information to entity fields, which can be configured below. Information will only be mapped if the entity field is empty.') . '</p>',
169       ],
170     ];
171
172     if (empty($source) || empty($source->getMetadataAttributes())) {
173       $form['source_dependent']['field_map']['#access'] = FALSE;
174     }
175     else {
176       $options = [MediaSourceInterface::METADATA_FIELD_EMPTY => $this->t('- Skip field -')];
177       foreach ($this->entityFieldManager->getFieldDefinitions('media', $this->entity->id()) as $field_name => $field) {
178         if (!($field instanceof BaseFieldDefinition) || $field_name === 'name') {
179           $options[$field_name] = $field->getLabel();
180         }
181       }
182
183       $field_map = $this->entity->getFieldMap();
184       foreach ($source->getMetadataAttributes() as $metadata_attribute_name => $metadata_attribute_label) {
185         $form['source_dependent']['field_map'][$metadata_attribute_name] = [
186           '#type' => 'select',
187           '#title' => $metadata_attribute_label,
188           '#options' => $options,
189           '#default_value' => isset($field_map[$metadata_attribute_name]) ? $field_map[$metadata_attribute_name] : MediaSourceInterface::METADATA_FIELD_EMPTY,
190         ];
191       }
192     }
193
194     $form['additional_settings'] = [
195       '#type' => 'vertical_tabs',
196       '#attached' => [
197         'library' => ['media/type_form'],
198       ],
199     ];
200
201     $form['workflow'] = [
202       '#type' => 'details',
203       '#title' => $this->t('Publishing options'),
204       '#group' => 'additional_settings',
205     ];
206
207     $form['workflow']['options'] = [
208       '#type' => 'checkboxes',
209       '#title' => $this->t('Default options'),
210       '#default_value' => $this->getWorkflowOptions(),
211       '#options' => [
212         'status' => $this->t('Published'),
213         'new_revision' => $this->t('Create new revision'),
214         'queue_thumbnail_downloads' => $this->t('Queue thumbnail downloads'),
215       ],
216     ];
217
218     $form['workflow']['options']['status']['#description'] = $this->t('Media will be automatically published when created.');
219     $form['workflow']['options']['new_revision']['#description'] = $this->t('Automatically create new revisions. Users with the "Administer media" permission will be able to override this option.');
220     $form['workflow']['options']['queue_thumbnail_downloads']['#description'] = $this->t('Download thumbnails via a queue. When using remote media sources, the thumbnail generation could be a slow process. Using a queue allows for this process to be handled in the background.');
221
222     if ($this->moduleHandler->moduleExists('language')) {
223       $form['language'] = [
224         '#type' => 'details',
225         '#title' => $this->t('Language settings'),
226         '#group' => 'additional_settings',
227       ];
228
229       $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('media', $this->entity->id());
230       $form['language']['language_configuration'] = [
231         '#type' => 'language_configuration',
232         '#entity_information' => [
233           'entity_type' => 'media',
234           'bundle' => $this->entity->id(),
235         ],
236         '#default_value' => $language_configuration,
237       ];
238     }
239
240     return $form;
241   }
242
243   /**
244    * Form submission handler to rebuild the form on select submit.
245    *
246    * @param array $form
247    *   Full form array.
248    * @param \Drupal\Core\Form\FormStateInterface $form_state
249    *   Current form state.
250    */
251   public static function rebuildSubmit(array &$form, FormStateInterface $form_state) {
252     $form_state->setRebuild();
253   }
254
255   /**
256    * Prepares workflow options to be used in the 'checkboxes' form element.
257    *
258    * @return array
259    *   Array of options ready to be used in #options.
260    */
261   protected function getWorkflowOptions() {
262     $workflow_options = [
263       'status' => $this->entity->getStatus(),
264       'new_revision' => $this->entity->shouldCreateNewRevision(),
265       'queue_thumbnail_downloads' => $this->entity->thumbnailDownloadsAreQueued(),
266     ];
267     // Prepare workflow options to be used for 'checkboxes' form element.
268     $keys = array_keys(array_filter($workflow_options));
269     return array_combine($keys, $keys);
270   }
271
272   /**
273    * Gets subform state for the media source configuration subform.
274    *
275    * @param array $form
276    *   Full form array.
277    * @param \Drupal\Core\Form\FormStateInterface $form_state
278    *   Parent form state.
279    *
280    * @return \Drupal\Core\Form\SubformStateInterface
281    *   Sub-form state for the media source configuration form.
282    */
283   protected function getSourceSubFormState(array $form, FormStateInterface $form_state) {
284     return SubformState::createForSubform($form['source_dependent']['source_configuration'], $form, $form_state)
285       ->set('operation', $this->operation)
286       ->set('type', $this->entity);
287   }
288
289   /**
290    * {@inheritdoc}
291    */
292   public function validateForm(array &$form, FormStateInterface $form_state) {
293     parent::validateForm($form, $form_state);
294
295     if (isset($form['source_dependent']['source_configuration'])) {
296       // Let the selected plugin validate its settings.
297       $this->entity->getSource()->validateConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
298     }
299   }
300
301   /**
302    * {@inheritdoc}
303    */
304   public function submitForm(array &$form, FormStateInterface $form_state) {
305     $form_state->setValue('field_map', array_filter(
306       $form_state->getValue('field_map', []),
307       function ($item) {
308         return $item != MediaSourceInterface::METADATA_FIELD_EMPTY;
309       }
310     ));
311
312     parent::submitForm($form, $form_state);
313
314     $this->entity->setQueueThumbnailDownloadsStatus((bool) $form_state->getValue(['options', 'queue_thumbnail_downloads']))
315       ->setStatus((bool) $form_state->getValue(['options', 'status']))
316       ->setNewRevision((bool) $form_state->getValue(['options', 'new_revision']));
317
318     if (isset($form['source_dependent']['source_configuration'])) {
319       // Let the selected plugin save its settings.
320       $this->entity->getSource()->submitConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
321     }
322   }
323
324   /**
325    * {@inheritdoc}
326    */
327   protected function actions(array $form, FormStateInterface $form_state) {
328     $actions = parent::actions($form, $form_state);
329     $actions['submit']['#value'] = $this->t('Save');
330     $actions['delete']['#value'] = $this->t('Delete');
331     $actions['delete']['#access'] = $this->entity->access('delete');
332     return $actions;
333   }
334
335   /**
336    * {@inheritdoc}
337    */
338   public function save(array $form, FormStateInterface $form_state) {
339     $status = parent::save($form, $form_state);
340     /** @var \Drupal\media\MediaTypeInterface $media_type */
341     $media_type = $this->entity;
342
343     // If the media source is using a source field, ensure it's
344     // properly created.
345     $source = $media_type->getSource();
346     $source_field = $source->getSourceFieldDefinition($media_type);
347     if (!$source_field) {
348       $source_field = $source->createSourceField($media_type);
349       /** @var \Drupal\field\FieldStorageConfigInterface $storage */
350       $storage = $source_field->getFieldStorageDefinition();
351       if ($storage->isNew()) {
352         $storage->save();
353       }
354       $source_field->save();
355
356       // Add the new field to the default form and view displays for this
357       // media type.
358       if ($source_field->isDisplayConfigurable('form')) {
359         // @todo Replace entity_get_form_display() when #2367933 is done.
360         // https://www.drupal.org/node/2872159.
361         $display = entity_get_form_display('media', $media_type->id(), 'default');
362         $source->prepareFormDisplay($media_type, $display);
363         $display->save();
364       }
365       if ($source_field->isDisplayConfigurable('view')) {
366         // @todo Replace entity_get_display() when #2367933 is done.
367         // https://www.drupal.org/node/2872159.
368         $display = entity_get_display('media', $media_type->id(), 'default');
369         $source->prepareViewDisplay($media_type, $display);
370         $display->save();
371       }
372     }
373
374     $t_args = ['%name' => $media_type->label()];
375     if ($status === SAVED_UPDATED) {
376       drupal_set_message($this->t('The media type %name has been updated.', $t_args));
377     }
378     elseif ($status === SAVED_NEW) {
379       drupal_set_message($this->t('The media type %name has been added.', $t_args));
380       $this->logger('media')->notice('Added media type %name.', $t_args);
381     }
382
383     // Override the "status" base field default value, for this media type.
384     $fields = $this->entityFieldManager->getFieldDefinitions('media', $media_type->id());
385     /** @var \Drupal\media\MediaInterface $media */
386     $media = $this->entityTypeManager->getStorage('media')->create(['bundle' => $media_type->id()]);
387     $value = (bool) $form_state->getValue(['options', 'status']);
388     if ($media->status->value != $value) {
389       $fields['status']->getConfig($media_type->id())->setDefaultValue($value)->save();
390     }
391
392     $form_state->setRedirectUrl($media_type->toUrl('collection'));
393   }
394
395 }