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/MediaBundleForm.php b/src/MediaBundleForm.php index 36365f3..3a0d121 100644 --- a/src/MediaBundleForm.php +++ b/src/MediaBundleForm.php @@ -313,6 +313,7 @@ class MediaBundleForm extends EntityForm { $plugin_configuration = !empty($this->configurableInstances[$plugin]['plugin_config']) ? $this->configurableInstances[$plugin]['plugin_config'] : []; $instance = $this->mediaTypeManager->createInstance($plugin, $plugin_configuration); $instance->submitConfigurationForm($form, $form_state); + } /** 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/MediaTypeInterface.php b/src/MediaTypeInterface.php index 2a171c2..4cda123 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,25 @@ 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); + + /** + * Create a default source field. + * + * Plugins defining media bundles are strongly encouraged to provide a default + * source field to all fields used as source, when a new bundle is created. + * + * @param string $bundle_name + * The machine-name of the bundle recently created. + */ + public function createDefaultSourceField($bundle_name); + } 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..18571f1 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,11 @@ 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\Plugin\MediaEntity\Type\Generic; /** @@ -17,6 +21,21 @@ use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; class TestType extends Generic { /** + * The name of the default source field on the media entity. + */ + const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME = 'field_media_test_type'; + + /** + * The id of the widget to be used when creating the default source field. + */ + const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_WIDGET = 'string_textfield'; + + /** + * The id of the formatter to be used when creating the default source field. + */ + const MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_FORMATTER = 'string'; + + /** * {@inheritdoc} */ public function providedFields() { @@ -32,6 +51,7 @@ class TestType extends Generic { public function defaultConfiguration() { return [ 'test_config_value' => 'This is default value.', + 'create_source_field' => TRUE, ]; } @@ -45,7 +65,96 @@ class TestType extends Generic { '#default_value' => empty($this->configuration['test_config_value']) ? NULL : $this->configuration['test_config_value'], ]; + $options = []; + $allowed_field_types = ['string']; + /** @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(); + } + } + + $form['source_field'] = array( + '#type' => 'select', + '#title' => $this->t('Field with source information'), + '#description' => $this->t('Field on media entity that stores the source information. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'), + '#default_value' => empty($this->configuration['source_field']) ? self::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME : $this->configuration['source_field'], + '#options' => $options, + ); + + // Add a checkbox to allow the field being created automatically on save. + if (empty($this->configuration['source_field'])) { + $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. You can change this setting later.'), + '#default_value' => $this->configuration['create_source_field'], + '#access' => $bundle->isNew(), + ]; + } + return $form; } + /** + * {@inheritdoc} + */ + public function reactOnBundleCreated($bundle_name, $entity_type_id) { + if (!empty($this->configuration['create_source_field'])) { + $this->createDefaultSourceField($bundle_name); + } + } + + /** + * {@inheritdoc} + */ + public function createDefaultSourceField($bundle_name) { + + // Create / load the field storage. + if (!$storage = FieldStorageConfig ::loadByName('media', static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME)) { + $storage = FieldStorageConfig::create([ + 'field_name' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, + 'entity_type' => 'media', + 'type' => 'string', + ]); + $storage->save(); + } + + // Create the field instance. + FieldConfig::create([ + 'entity_type' => 'media', + 'field_name' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, + 'label' => $this->t('Tweet URL'), + '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(static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, [ + 'type' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_WIDGET, + ])->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(static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME, [ + 'type' => static::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_FORMATTER, + ])->save(); + + // @TODO Evaluate if it's necessary to save the 'source_field' config value. + } + } diff --git a/tests/src/FunctionalJavascript/BundleCreationTest.php b/tests/src/FunctionalJavascript/BundleCreationTest.php new file mode 100644 index 0000000..13aaf75 --- /dev/null +++ b/tests/src/FunctionalJavascript/BundleCreationTest.php @@ -0,0 +1,60 @@ +getSession()->getPage(); + + $this->drupalGet('admin/structure/media/add'); + + // Fill in a label to the bundle. + $page->fillField('label', 'Foo bundle'); + + // Select our test bundle type. + $this->assertSession()->fieldExists('Type provider'); + $this->assertSession()->optionExists('Type provider', 'test_type'); + $page->selectFieldOption('Type provider', 'test_type'); + $this->waitForAjaxToFinish(); +$this->saveHtmlOutput(); // @TODO Delete me before committing. + + // Make sure the checkbox for creating the source field is there and save. + $this->assertSession()->checkboxChecked('type_configuration[test_type][create_source_field]'); + $page->pressButton('Save media bundle'); +$this->saveHtmlOutput(); // @TODO Delete me before committing. + + // Check whether the source field was correctly created. + // @TODO Finish me. + $this->assertNotNull(FieldStorageConfig::loadByName('media', TestType::MEDIA_ENTITY_TEST_TYPE_DEFAULT_FIELD_NAME)); + } + +} diff --git a/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php b/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php new file mode 100644 index 0000000..b47a672 --- /dev/null +++ b/tests/src/FunctionalJavascript/MediaEntityJavascriptTestBase.php @@ -0,0 +1,91 @@ +drupalCreateUser([ + 'administer media', + 'administer media bundles', + // Media entity permissions. + 'view media', + 'create media', + 'update media', + 'update any media', + 'delete media', + 'delete any media', + 'access media overview', + ]); + $this->drupalLogin($account); + } + + /** + * Waits for jQuery to become ready and animations to complete. + */ + protected function waitForAjaxToFinish() { + $this->assertSession()->assertWaitOnAjaxRequest(); + } + + /** + * Waits and asserts that a given element is visible. + * + * @param string $selector + * The CSS selector. + * @param int $timeout + * (Optional) Timeout in milliseconds, defaults to 1000. + * @param string $message + * (Optional) Message to pass to assertJsCondition(). + */ + protected function waitUntilVisible($selector, $timeout = 1000, $message = '') { + $condition = "jQuery('" . $selector . ":visible').length > 0"; + $this->assertJsCondition($condition, $timeout, $message); + } + + /** + * Debugger method to save additional HTML output. + * + * The base class will only save browser output when accessing page using + * ::drupalGet and providing a printer class to PHPUnit. This method + * is intended for developers to help debug browser test failures and capture + * more verbose output. + */ + protected function saveHtmlOutput() { + $out = $this->getSession()->getPage()->getContent(); + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + if ($this->htmlOutputEnabled) { + $html_output = '
Ending URL: ' . $this->getSession()->getCurrentUrl(); + $html_output .= '
' . $out; + $html_output .= $this->getHtmlOutputHeaders(); + $this->htmlOutput($html_output); + } + } + +}