diff --git a/src/Entity/MediaBundle.php b/src/Entity/MediaBundle.php index 8833f82..d8ad3d7 100644 --- a/src/Entity/MediaBundle.php +++ b/src/Entity/MediaBundle.php @@ -2,8 +2,9 @@ namespace Drupal\media_entity\Entity; -use Drupal\Core\Entity\EntityDescriptionInterface; use Drupal\Core\Config\Entity\ConfigEntityBundleBase; +use Drupal\Core\Entity\EntityDescriptionInterface; +use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; use Drupal\media_entity\MediaBundleInterface; @@ -121,7 +122,7 @@ class MediaBundle extends ConfigEntityBundleBase implements MediaBundleInterface /** * Default status of this media bundle. * - * @var array + * @var bool */ public $status = TRUE; @@ -241,4 +242,17 @@ class MediaBundle extends ConfigEntityBundleBase implements MediaBundleInterface $this->new_revision = $new_revision; } + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + if (!$update) { + // Inform the plugin that this bundle has just been created. + $this->getType()->reactOnBundleCreated($this->id(), $this->getEntityTypeId()); + } + + } + } diff --git a/src/MediaTypeBase.php b/src/MediaTypeBase.php index a472e7d..87ef61b 100644 --- a/src/MediaTypeBase.php +++ b/src/MediaTypeBase.php @@ -3,14 +3,14 @@ namespace Drupal\media_entity; use Drupal\Component\Plugin\PluginBase; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Config\Config; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\Component\Utility\NestedArray; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\Core\Form\FormStateInterface; /** * Base implementation of media type plugin. @@ -158,4 +158,14 @@ abstract class MediaTypeBase extends PluginBase implements MediaTypeInterface, C return 'media:' . $media->bundle() . ':' . $media->uuid(); } + /** + * {@inheritdoc} + */ + public function reactOnBundleCreated($bundle_name, $entity_type_id) {} + + /** + * {@inheritdoc} + */ + public function createDefaultSourceField($bundle_name) {} + } diff --git a/src/MediaTypeDefaultFieldTrait.php b/src/MediaTypeDefaultFieldTrait.php new file mode 100644 index 0000000..70053ff --- /dev/null +++ b/src/MediaTypeDefaultFieldTrait.php @@ -0,0 +1,210 @@ + $this->t('- None -')]; + $allowed_field_types = $this->allowedSourceFieldTypes(); + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + $bundle = $form_state->getFormObject()->getEntity(); + foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) { + if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition() + ->isBaseField() + ) { + $options[$field_name] = $field->getLabel(); + } + } + + // Add a checkbox to allow the field being created automatically on save. + // Only include it when the bundle is new, so we can use its presence to + // know we can safely override the source field configuration. + if ($bundle->isNew()) { + $form['create_source_field'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create a source field automatically when saving this form.'), + '#description' => $this->t('If checked, a default field will be created and used as a source field. If you uncheck the field, you will need to create a field and revisit this form later to select it.'), + '#default_value' => TRUE, + ]; + } + + // Select the source field. Only show it when the bundle is not new, so + // there will potentially be fields to select. + $form['source_field'] = array( + '#type' => 'select', + '#title' => $this->t('Field with source information'), + '#default_value' => $this->configuration['source_field'], + '#options' => $options, + '#access' => !$bundle->isNew(), + ); + + + return $form; + } + + /** + * {@inheritdoc} + */ + public function reactOnBundleCreated($bundle_name, $entity_type_id) { + if (isset($this->defaultSourceFieldName)) { + $this->createDefaultSourceField($bundle_name); + } + } + + /** + * {@inheritdoc} + */ + public function createDefaultSourceField($bundle_name) { + + if (!in_array($this->defaultSourceFieldType(), $this->allowedSourceFieldTypes())) { + throw new \LogicException('Default source field type is not within the allowed field types.'); + } + + $field_name = $this->defaultSourceFieldName; + + $storage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'media', + 'type' => $this->defaultSourceFieldType(), + ]); + $storage->save(); + + // Create the field instance. + FieldConfig::create([ + 'entity_type' => 'media', + 'field_name' => $field_name, + 'label' => $this->defaultSourceFieldLabel(), + 'required' => TRUE, + 'bundle' => $bundle_name, + ])->save(); + + // Make the field visible on the form display. + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = EntityFormDisplay::create([ + 'targetEntityType' => 'media', + 'bundle' => $bundle_name, + 'mode' => 'default', + 'status' => TRUE, + ]); + $form_display->setComponent($field_name, [ + 'type' => $this->defaultSourceFieldWidget(), + ])->save(); + + // Make the field visible on the media entity itself. + /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ + $display = EntityViewDisplay::create([ + 'targetEntityType' => 'media', + 'bundle' => $bundle_name, + 'mode' => 'default', + 'status' => TRUE, + ]); + $display->setComponent($field_name, [ + 'type' => $this->defaultSourceFieldFormatter(), + ])->save(); + } + + /** + * Determine a free field name to use as the default field. + * + * @return string + * An appropriate field name that was determined to be available. + */ + protected function determineDefaultSourceFieldName() { + // Determine a unique field name. + if (isset($this->defaultSourceFieldName)) { + return $this->defaultSourceFieldName; + } + + $base_field_name = 'field_media_' . $this->getPluginId(); + $field_name_candidate = $base_field_name; + $postfix = ''; + while (FieldStorageConfig::loadByName('media', $field_name_candidate)) { + if (empty($postfix)) { + $postfix = 1; + } + else { + $postfix++; + } + + $field_name_candidate = $base_field_name . '_' . $postfix; + } + + return $this->defaultSourceFieldName = $field_name_candidate; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + // Take out the create_source_field key when present so it doesn't end up + // in the configuration. Record it was set so we can later decide to + // actually create the field. Determine the field name here so it correctly + // is entered into the configuration. + if (isset($configuration['create_source_field'])) { + unset($configuration['create_source_field']); + $configuration['source_field'] = $this->determineDefaultSourceFieldName(); + } + + parent::setConfiguration($configuration); + } + +} diff --git a/src/MediaTypeInterface.php b/src/MediaTypeInterface.php index 2a171c2..632dcec 100644 --- a/src/MediaTypeInterface.php +++ b/src/MediaTypeInterface.php @@ -2,8 +2,8 @@ namespace Drupal\media_entity; -use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Plugin\PluginFormInterface; /** @@ -85,4 +85,14 @@ interface MediaTypeInterface extends PluginInspectionInterface, ConfigurablePlug */ public function getDefaultName(MediaInterface $media); + /** + * Take actions on bundle creation. + * + * @param string $bundle_name + * The machine-name of the bundle created. + * @param string $entity_type_id + * The entity type id. + */ + public function reactOnBundleCreated($bundle_name, $entity_type_id); + } diff --git a/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php b/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php index 60d68a0..5ba6800 100644 --- a/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php +++ b/tests/modules/media_entity_test_type/src/Plugin/MediaEntity/Type/TestType.php @@ -2,7 +2,12 @@ namespace Drupal\media_entity_test_type\Plugin\MediaEntity\Type; +use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Form\FormStateInterface; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\media_entity\MediaTypeDefaultFieldTrait; use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; /** @@ -15,6 +20,42 @@ use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; * ) */ class TestType extends Generic { + use MediaTypeDefaultFieldTrait; + + /** + * {@inheritdoc} + */ + public function allowedSourceFieldTypes() { + return ['string']; + } + + /** + * {@inheritdoc} + */ + public function defaultSourceFieldLabel() { + return $this->t('Test source'); + } + + /** + * {@inheritdoc} + */ + public function defaultSourceFieldType() { + return 'string'; + } + + /** + * {@inheritdoc} + */ + public function defaultSourceFieldWidget() { + return 'string_textfield'; + } + + /** + * {@inheritdoc} + */ + public function defaultSourceFieldFormatter() { + return 'string'; + } /** * {@inheritdoc} @@ -32,6 +73,7 @@ class TestType extends Generic { public function defaultConfiguration() { return [ 'test_config_value' => 'This is default value.', + 'create_source_field' => TRUE, ]; } @@ -45,6 +87,10 @@ class TestType extends Generic { '#default_value' => empty($this->configuration['test_config_value']) ? NULL : $this->configuration['test_config_value'], ]; + $form = $this->defaultFieldConfigurationForm($form, $form_state); + + $form['source_field']['#description'] = $this->t('Field on media entity that stores the source information.'); + return $form; }