diff --git a/extra_field.api.php b/extra_field.api.php index cff967e..08b3c34 100644 --- a/extra_field.api.php +++ b/extra_field.api.php @@ -41,6 +41,18 @@ function hook_extra_field_display_info_alter(array &$info) { $info['all_nodes']['bundles'][] = 'taxonomy_term.*'; } +/** + * Perform alterations on Extra Field Form Displays. + * + * @param array $info + * An array of information on existing Extra Field Form Displays, as collected + * by the annotation discovery mechanism. + */ +function hook_extra_field_form_info_alter(array &$info) { + // Let a plugin also be used for all taxonomy terms. + $info['all_nodes']['bundles'][] = 'taxonomy_term.*'; +} + /** * @} End of "addtogroup hooks". */ diff --git a/extra_field.module b/extra_field.module index d0140d7..7fcd1b5 100644 --- a/extra_field.module +++ b/extra_field.module @@ -5,15 +5,22 @@ * Hook implementations for Extra Field module. */ +use Drupal\Core\Entity\ContentEntityFormInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Form\ConfirmFormInterface; +use Drupal\Core\Form\FormStateInterface; /** * Implements hook_entity_extra_field_info(). */ function extra_field_entity_extra_field_info() { + $info_form = \Drupal::service('plugin.manager.extra_field_form_display') + ->fieldInfo(); + $info_display = \Drupal::service('plugin.manager.extra_field_display') + ->fieldInfo(); - return \Drupal::service('plugin.manager.extra_field_display')->fieldInfo(); + return array_merge_recursive($info_form, $info_display); } /** @@ -24,3 +31,16 @@ function extra_field_entity_view(array &$build, EntityInterface $entity, EntityV /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ \Drupal::service('plugin.manager.extra_field_display')->entityView($build, $entity, $display, $view_mode); } + +/** + * Implements hook_form_alter(). + */ +function extra_field_form_alter(array &$form, FormStateInterface $form_state) { + $form_object = $form_state->getFormObject(); + if ($form_object instanceof ContentEntityFormInterface + && !$form_object instanceof ConfirmFormInterface + ) { + $display = $form_object->getFormDisplay($form_state); + \Drupal::service('plugin.manager.extra_field_form_display')->entityFormAlter($display, $form, $form_state); + } +} diff --git a/extra_field.services.yml b/extra_field.services.yml index bb9951f..809597a 100644 --- a/extra_field.services.yml +++ b/extra_field.services.yml @@ -2,3 +2,7 @@ services: plugin.manager.extra_field_display: class: Drupal\extra_field\Plugin\ExtraFieldDisplayManager arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@entity_type.manager'] + + plugin.manager.extra_field_form: + class: Drupal\extra_field\Plugin\ExtraFieldFormManager + arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@entity_type.manager'] diff --git a/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleAllNodes.php b/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleAllNodes.php new file mode 100644 index 0000000..ef8d479 --- /dev/null +++ b/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleAllNodes.php @@ -0,0 +1,53 @@ + 'submit', + '#value' => $this->t('Custom submit'), + '#submit' => [ + '::submitForm', + [self::class, 'customSubmit'], + ], + ]; + + return $element; + } + + /** + * Handle custom submit. + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. + */ + public static function customSubmit(array $form, FormStateInterface $form_state) { + // TODO Don't use deprecated functions (or example without dependency) + drupal_set_message('Custom submit was triggered.'); + } + +} diff --git a/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleFormAllNodes.php b/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleFormAllNodes.php new file mode 100644 index 0000000..fefd004 --- /dev/null +++ b/modules/extra_field_example/src/Plugin/ExtraField/Form/ExampleFormAllNodes.php @@ -0,0 +1,52 @@ + 'submit', + '#value' => $this->t('Custom submit'), + '#submit' => [ + '::submitForm', + [self::class, 'customSubmit'], + ], + ]; + + return $element; + } + + /** + * Custom submit. + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. + */ + public static function customSubmit(array $form, FormStateInterface $form_state) { + drupal_set_message('Custom submit was triggered.'); + } + +} diff --git a/src/Annotation/ExtraFieldForm.php b/src/Annotation/ExtraFieldForm.php new file mode 100644 index 0000000..f150cbc --- /dev/null +++ b/src/Annotation/ExtraFieldForm.php @@ -0,0 +1,56 @@ +entityFormDisplay = $display; + } + + /** + * {@inheritdoc} + */ + public function getEntityFormDisplay() { + return $this->entityFormDisplay; + } + + /** + * {@inheritdoc} + */ + public function setFormMode($form_mode) { + $this->formMode = $form_mode; + } + + /** + * {@inheritdoc} + */ + public function getFormMode() { + return $this->formMode; + } + +} diff --git a/src/Plugin/ExtraFieldFormInterface.php b/src/Plugin/ExtraFieldFormInterface.php new file mode 100644 index 0000000..78e78e8 --- /dev/null +++ b/src/Plugin/ExtraFieldFormInterface.php @@ -0,0 +1,60 @@ +alterInfo('extra_field_form_info'); + $this->setCacheBackend($cache_backend, 'extra_field_form_plugins'); + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public function fieldInfo() { + $info = []; + $definitions = $this->getDefinitions(); + + foreach ($definitions as $key => $definition) { + $entityBundles = $this->supportedEntityBundles($definition['bundles']); + + foreach ($entityBundles as $entityBundle) { + $entity_type = $entityBundle['entity']; + $bundle = $entityBundle['bundle']; + $fieldName = $this->fieldName($key); + $info[$entity_type][$bundle]['form'][$fieldName] = [ + 'label' => $definition['label'], + 'weight' => $definition['weight'], + 'visible' => $definition['visible'], + ]; + } + } + + return $info; + } + + /** + * {@inheritdoc} + */ + public function entityFormAlter(EntityFormDisplayInterface $display, array &$form, FormStateInterface $formState) { + $entityType = $display->getTargetEntityTypeId(); + $entityBundleKey = $this->entityBundleKey($entityType, $display->get('bundle')); + foreach ($this->getDefinitions() as $definition) { + if ($this->matchEntityBundleKey($definition['bundles'], $entityBundleKey)) { + + $factory = $this->getFactory(); + $pluginId = $definition['id']; + if ($extraField = $display->getComponent($this->fieldName($pluginId))) { + + /** @var ExtraFieldFormInterface $plugin */ + $plugin = $factory->createInstance($pluginId); + $fieldName = $this->fieldName($pluginId); + $plugin->setEntityFormDisplay($display); + $plugin->setFormMode($display->get('mode')); + $element = $plugin->view($form, $formState); + if (!empty($element)) { + $form[$fieldName] = [ + '#weight' => $extraField['weight'], + '#type' => 'container', + 'widget' => $element, + ]; + } + } + } + } + } + + /** + * Checks if the plugin bundle definition matches the entity bundle key. + * + * TODO: Move this method to ExtraFieldManagerTrait. + * + * @param string[] $pluginBundles + * Defines which entity-bundle pair the plugin can be used for. + * Format: [entity type].[bundle] or [entity type].* . + * @param string $entityBundleKey + * The entity-bundle string of a content entity. + * Format: [entity type].[bundle] . + * + * @return bool + * True of the plugin bundle definition matches the entity bundle key. + */ + protected function matchEntityBundleKey(array $pluginBundles, $entityBundleKey) { + + $match = FALSE; + foreach ($pluginBundles as $pluginBundle) { + if (strpos($pluginBundle, '.*')) { + $match = explode('.', $pluginBundle)[0] == explode('.', $entityBundleKey)[0]; + } + else { + $match = $pluginBundle == $entityBundleKey; + } + + if ($match) { + break; + } + } + + return $match; + } + + /** + * Returns entity-bundle combinations this plugin supports. + * + * If a wildcard bundle is set, all bundles of the entity will be included. + * TODO: Move this method to ExtraFieldManagerTrait. + * + * @param string[] $entityBundleKeys + * Array of entity-bundle strings that define the bundles for which the + * plugin can be used. Format: [entity].[bundle] + * '*' can be used as bundle wildcard. + * + * @return array + * Array of entity and bundle names. Keyed by the [entity].[bundle] key. + */ + protected function supportedEntityBundles(array $entityBundleKeys) { + + $result = []; + foreach ($entityBundleKeys as $entityBundleKey) { + if (strpos($entityBundleKey, '.')) { + list($entityType, $bundle) = explode('.', $entityBundleKey); + if ($bundle == '*') { + foreach ($this->allEntityBundles($entityType) as $bundle) { + $key = $this->entityBundleKey($entityType, $bundle); + $result[$key] = [ + 'entity' => $entityType, + 'bundle' => $bundle, + ]; + } + } + else { + $result[$entityBundleKey] = [ + 'entity' => $entityType, + 'bundle' => $bundle, + ]; + } + } + } + + return $result; + } + + /** + * Returns the bundles that are defined for an entity type. + * + * TODO: Move this method to ExtraFieldManagerTrait. + * + * @param string $entityType + * The entity type to get the bundles for. + * + * @return string[] + * Array of bundle names. + */ + protected function allEntityBundles($entityType) { + + if (!isset($this->entityBundles[$entityType])) { + $bundleType = $this->entityTypeManager + ->getDefinition($entityType) + ->getBundleEntityType(); + + if ($bundleType) { + $bundles = $this->entityTypeManager + ->getStorage($bundleType) + ->getQuery() + ->execute(); + } + else { + $bundles = [$entityType => $entityType]; + } + $this->entityBundles[$entityType] = $bundles; + } + + return $this->entityBundles[$entityType]; + } + + /** + * Build the field name string. + * + * TODO: Move this method to ExtraFieldManagerTrait. + * + * @param string $pluginId + * The machine name of the Extra Field plugin. + * + * @return string + * Field name. + */ + protected function fieldName($pluginId) { + + return 'extra_field_' . $pluginId; + } + + /** + * Creates a key string with entity type and bundle. + * + * TODO: Move this method to ExtraFieldManagerTrait. + * + * @param string $entityType + * The entity type. + * @param string $bundle + * The bundle. + * + * @return string + * Formatted string. + */ + protected function entityBundleKey($entityType, $bundle) { + + return "$entityType.$bundle"; + } + +} diff --git a/src/Plugin/ExtraFieldFormManagerInterface.php b/src/Plugin/ExtraFieldFormManagerInterface.php new file mode 100644 index 0000000..ee3ba55 --- /dev/null +++ b/src/Plugin/ExtraFieldFormManagerInterface.php @@ -0,0 +1,37 @@ +