3 namespace Drupal\media;
5 use Drupal\Component\Plugin\PluginManagerInterface;
6 use Drupal\Core\Ajax\AjaxResponse;
7 use Drupal\Core\Ajax\ReplaceCommand;
8 use Drupal\Core\Entity\EntityFieldManagerInterface;
9 use Drupal\Core\Entity\EntityForm;
10 use Drupal\Core\Field\BaseFieldDefinition;
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\Form\SubformState;
13 use Drupal\language\Entity\ContentLanguageSettings;
14 use Drupal\media\Entity\MediaType;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
18 * Form controller for media type forms.
22 class MediaTypeForm extends EntityForm {
25 * Media source plugin manager.
27 * @var \Drupal\Component\Plugin\PluginManagerInterface
29 protected $sourceManager;
32 * Entity field manager service.
34 * @var \Drupal\Core\Entity\EntityFieldManagerInterface
36 protected $entityFieldManager;
39 * Constructs a new class instance.
41 * @param \Drupal\Component\Plugin\PluginManagerInterface $source_manager
42 * Media source plugin manager.
43 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
44 * Entity field manager service.
46 public function __construct(PluginManagerInterface $source_manager, EntityFieldManagerInterface $entity_field_manager) {
47 $this->sourceManager = $source_manager;
48 $this->entityFieldManager = $entity_field_manager;
54 public static function create(ContainerInterface $container) {
56 $container->get('plugin.manager.media.source'),
57 $container->get('entity_field.manager')
62 * Ajax callback triggered by the type provider select element.
64 public function ajaxHandlerData(array $form, FormStateInterface $form_state) {
65 $response = new AjaxResponse();
66 $response->addCommand(new ReplaceCommand('#source-dependent', $form['source_dependent']));
73 public function form(array $form, FormStateInterface $form_state) {
74 $form = parent::form($form, $form_state);
76 // Source is not set when the entity is initially created.
77 /** @var \Drupal\media\MediaSourceInterface $source */
78 $source = $this->entity->get('source') ? $this->entity->getSource() : NULL;
80 if ($this->operation === 'add') {
81 $form['#title'] = $this->t('Add media type');
85 '#title' => $this->t('Name'),
86 '#type' => 'textfield',
87 '#default_value' => $this->entity->label(),
88 '#description' => $this->t('The human-readable name of this media type.'),
94 '#type' => 'machine_name',
95 '#default_value' => $this->entity->id(),
97 '#disabled' => !$this->entity->isNew(),
99 'exists' => [MediaType::class, 'load'],
101 '#description' => $this->t('A unique machine-readable name for this media type.'),
104 $form['description'] = [
105 '#title' => $this->t('Description'),
106 '#type' => 'textarea',
107 '#default_value' => $this->entity->getDescription(),
108 '#description' => $this->t('Describe this media type. The text will be displayed on the <em>Add new media</em> page.'),
111 $plugins = $this->sourceManager->getDefinitions();
113 foreach ($plugins as $plugin_id => $definition) {
114 $options[$plugin_id] = $definition['label'];
117 $form['source_dependent'] = [
118 '#type' => 'container',
119 '#attributes' => ['id' => 'source-dependent'],
122 if (!$this->entity->isNew()) {
123 $source_description = $this->t('<em>The media source cannot be changed after the media type is created.</em>');
126 $source_description = $this->t('Media source that is responsible for additional logic related to this media type.');
128 $form['source_dependent']['source'] = [
130 '#title' => $this->t('Media source'),
131 '#default_value' => $source ? $source->getPluginId() : NULL,
132 '#options' => $options,
133 '#description' => $source_description,
134 '#ajax' => ['callback' => '::ajaxHandlerData'],
136 // Once the media type is created, its source plugin cannot be changed
138 '#disabled' => !$this->entity->isNew(),
142 // Media source plugin configuration.
143 $form['source_dependent']['source_configuration'] = [
144 '#type' => 'fieldset',
145 '#title' => $this->t('Media source configuration'),
149 $form['source_dependent']['source_configuration'] = $source->buildConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
152 // Field mapping configuration.
153 $form['source_dependent']['field_map'] = [
154 '#type' => 'fieldset',
155 '#title' => $this->t('Field mapping'),
158 '#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>',
162 if (empty($source) || empty($source->getMetadataAttributes())) {
163 $form['source_dependent']['field_map']['#access'] = FALSE;
166 $options = [MediaSourceInterface::METADATA_FIELD_EMPTY => $this->t('- Skip field -')];
167 foreach ($this->entityFieldManager->getFieldDefinitions('media', $this->entity->id()) as $field_name => $field) {
168 if (!($field instanceof BaseFieldDefinition) || $field_name === 'name') {
169 $options[$field_name] = $field->getLabel();
173 $field_map = $this->entity->getFieldMap();
174 foreach ($source->getMetadataAttributes() as $metadata_attribute_name => $metadata_attribute_label) {
175 $form['source_dependent']['field_map'][$metadata_attribute_name] = [
177 '#title' => $metadata_attribute_label,
178 '#options' => $options,
179 '#default_value' => isset($field_map[$metadata_attribute_name]) ? $field_map[$metadata_attribute_name] : MediaSourceInterface::METADATA_FIELD_EMPTY,
184 $form['additional_settings'] = [
185 '#type' => 'vertical_tabs',
187 'library' => ['media/type_form'],
191 $form['workflow'] = [
192 '#type' => 'details',
193 '#title' => $this->t('Publishing options'),
194 '#group' => 'additional_settings',
197 $form['workflow']['options'] = [
198 '#type' => 'checkboxes',
199 '#title' => $this->t('Default options'),
200 '#default_value' => $this->getWorkflowOptions(),
202 'status' => $this->t('Published'),
203 'new_revision' => $this->t('Create new revision'),
204 'queue_thumbnail_downloads' => $this->t('Queue thumbnail downloads'),
208 $form['workflow']['options']['status']['#description'] = $this->t('Media will be automatically published when created.');
209 $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.');
210 $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.');
212 if ($this->moduleHandler->moduleExists('language')) {
213 $form['language'] = [
214 '#type' => 'details',
215 '#title' => $this->t('Language settings'),
216 '#group' => 'additional_settings',
219 $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('media', $this->entity->id());
220 $form['language']['language_configuration'] = [
221 '#type' => 'language_configuration',
222 '#entity_information' => [
223 'entity_type' => 'media',
224 'bundle' => $this->entity->id(),
226 '#default_value' => $language_configuration,
234 * Prepares workflow options to be used in the 'checkboxes' form element.
237 * Array of options ready to be used in #options.
239 protected function getWorkflowOptions() {
240 $workflow_options = [
241 'status' => $this->entity->getStatus(),
242 'new_revision' => $this->entity->shouldCreateNewRevision(),
243 'queue_thumbnail_downloads' => $this->entity->thumbnailDownloadsAreQueued(),
245 // Prepare workflow options to be used for 'checkboxes' form element.
246 $keys = array_keys(array_filter($workflow_options));
247 return array_combine($keys, $keys);
251 * Gets subform state for the media source configuration subform.
255 * @param \Drupal\Core\Form\FormStateInterface $form_state
258 * @return \Drupal\Core\Form\SubformStateInterface
259 * Sub-form state for the media source configuration form.
261 protected function getSourceSubFormState(array $form, FormStateInterface $form_state) {
262 return SubformState::createForSubform($form['source_dependent']['source_configuration'], $form, $form_state)
263 ->set('operation', $this->operation)
264 ->set('type', $this->entity);
270 public function validateForm(array &$form, FormStateInterface $form_state) {
271 parent::validateForm($form, $form_state);
273 if (isset($form['source_dependent']['source_configuration'])) {
274 // Let the selected plugin validate its settings.
275 $this->entity->getSource()->validateConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
282 public function submitForm(array &$form, FormStateInterface $form_state) {
283 $form_state->setValue('field_map', array_filter(
284 $form_state->getValue('field_map', []),
286 return $item != MediaSourceInterface::METADATA_FIELD_EMPTY;
290 parent::submitForm($form, $form_state);
292 $this->entity->setQueueThumbnailDownloadsStatus((bool) $form_state->getValue(['options', 'queue_thumbnail_downloads']))
293 ->setStatus((bool) $form_state->getValue(['options', 'status']))
294 ->setNewRevision((bool) $form_state->getValue(['options', 'new_revision']));
296 if (isset($form['source_dependent']['source_configuration'])) {
297 // Let the selected plugin save its settings.
298 $this->entity->getSource()->submitConfigurationForm($form['source_dependent']['source_configuration'], $this->getSourceSubFormState($form, $form_state));
305 protected function actions(array $form, FormStateInterface $form_state) {
306 $actions = parent::actions($form, $form_state);
307 $actions['submit']['#value'] = $this->t('Save');
308 $actions['delete']['#value'] = $this->t('Delete');
309 $actions['delete']['#access'] = $this->entity->access('delete');
316 public function save(array $form, FormStateInterface $form_state) {
317 $status = parent::save($form, $form_state);
318 /** @var \Drupal\media\MediaTypeInterface $media_type */
319 $media_type = $this->entity;
321 // If the media source is using a source field, ensure it's
323 $source = $media_type->getSource();
324 $source_field = $source->getSourceFieldDefinition($media_type);
325 if (!$source_field) {
326 $source_field = $source->createSourceField($media_type);
327 /** @var \Drupal\field\FieldStorageConfigInterface $storage */
328 $storage = $source_field->getFieldStorageDefinition();
329 if ($storage->isNew()) {
332 $source_field->save();
334 // Add the new field to the default form and view displays for this
336 if ($source_field->isDisplayConfigurable('form')) {
337 // @todo Replace entity_get_form_display() when #2367933 is done.
338 // https://www.drupal.org/node/2872159.
339 $display = entity_get_form_display('media', $media_type->id(), 'default');
340 $source->prepareFormDisplay($media_type, $display);
343 if ($source_field->isDisplayConfigurable('view')) {
344 // @todo Replace entity_get_display() when #2367933 is done.
345 // https://www.drupal.org/node/2872159.
346 $display = entity_get_display('media', $media_type->id(), 'default');
347 $source->prepareViewDisplay($media_type, $display);
352 $t_args = ['%name' => $media_type->label()];
353 if ($status === SAVED_UPDATED) {
354 $this->messenger()->addStatus($this->t('The media type %name has been updated.', $t_args));
356 elseif ($status === SAVED_NEW) {
357 $this->messenger()->addStatus($this->t('The media type %name has been added.', $t_args));
358 $this->logger('media')->notice('Added media type %name.', $t_args);
361 // Override the "status" base field default value, for this media type.
362 $fields = $this->entityFieldManager->getFieldDefinitions('media', $media_type->id());
363 /** @var \Drupal\media\MediaInterface $media */
364 $media = $this->entityTypeManager->getStorage('media')->create(['bundle' => $media_type->id()]);
365 $value = (bool) $form_state->getValue(['options', 'status']);
366 if ($media->status->value != $value) {
367 $fields['status']->getConfig($media_type->id())->setDefaultValue($value)->save();
370 $form_state->setRedirectUrl($media_type->toUrl('collection'));