diff --git a/paragraphs.api.php b/paragraphs.api.php
index 78403df..ae1cc85 100644
--- a/paragraphs.api.php
+++ b/paragraphs.api.php
@@ -48,5 +48,116 @@ function hook_paragraphs_widget_actions_alter(array &$widget_actions, array &$co
}
/**
+ * Perform alterations on a paragraphs entity subform.
+ *
+ * Modules can implement hook_form_paragraphs_subform_alter() to change a
+ * paragraphs entity subform in the entity reference widgets.
+ *
+ * In addition to hook_form_paragraphs_subform_alter(), there are more specific
+ * form hooks available. This allows targeting of a specific widget type and/or
+ * paragraphs type form directly. Within each module, the alter hooks are
+ * called in the following order:
+ * - hook_form_paragraphs_subform_alter()
+ * - hook_form_paragraphs_subform_TYPE_alter()
+ * With TYPE being the paragraphs type of the paragraphs entity.
+ * - hook_form_paragraphs_subform_WIDGET_alter()
+ * With WIDGET being 'classic' or 'experimental'.
+ * - hook_form_paragraphs_subform_WIDGET_TYPE_alter()
+ * With WIDGET being 'classic' or 'experimental' and TYPE being the
+ * paragraphs type of the paragraphs entity.
+ *
+ * @param array $subform
+ * Nested array of form elements for the paragraphs entity subform in the
+ * widget.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form. The arguments that
+ * \Drupal::formBuilder()->getForm() was originally called with are available
+ * in the array $form_state->getBuildInfo()['args'].
+ * @param int $delta
+ * The order of this item in the array of sub-elements (0, 1, 2, etc.).
+ *
+ * @see hook_form_paragraphs_subform_TYPE_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_TYPE_alter()
+ */
+function hook_form_paragraphs_subform_alter(array &$subform, \Drupal\Core\Form\FormStateInterface $form_state, $delta) {
+ $paragraph = $form_state->get('paragraph');
+}
+
+/**
+ * Perform alterations on a paragraphs entity subform.
+ *
+ * Modules can implement hook_form_paragraphs_subform_TYPE_alter() to change
+ * a paragraphs entity subform in the entity reference widgets for a specific
+ * paragraphs type.
+ *
+ * @param array $subform
+ * Nested array of form elements for the paragraphs entity subform in the
+ * widget.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form. The arguments that
+ * \Drupal::formBuilder()->getForm() was originally called with are available
+ * in the array $form_state->getBuildInfo()['args'].
+ * @param int $delta
+ * The order of this item in the array of sub-elements (0, 1, 2, etc.).
+ *
+ * @see hook_form_paragraphs_subform_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_TYPE_alter()
+ */
+function hook_form_paragraphs_subform_TYPE_alter(array &$subform, \Drupal\Core\Form\FormStateInterface $form_state, $delta) {
+ $paragraph = $form_state->get('paragraph');
+}
+
+/**
+ * Perform alterations on a paragraphs entity subform.
+ *
+ * Modules can implement hook_form_paragraphs_subform_WIDGET_alter() to change
+ * a paragraphs entity subform in a specific entity reference widget.
+ *
+ * @param array $subform
+ * Nested array of form elements for the paragraphs entity subform in the
+ * widget.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form. The arguments that
+ * \Drupal::formBuilder()->getForm() was originally called with are available
+ * in the array $form_state->getBuildInfo()['args'].
+ * @param int $delta
+ * The order of this item in the array of sub-elements (0, 1, 2, etc.).
+ *
+ * @see hook_form_paragraphs_subform_alter()
+ * @see hook_form_paragraphs_subform_TYPE_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_TYPE_alter()
+ */
+function hook_form_paragraphs_subform_WIDGET_alter(array &$subform, \Drupal\Core\Form\FormStateInterface $form_state, $delta) {
+ $paragraph = $form_state->get('paragraph');
+}
+
+/**
+ * Perform alterations on a paragraphs entity subform.
+ *
+ * Modules can implement hook_form_paragraphs_subform_WIDGET_TYPE_alter() to
+ * change a paragraphs entity subform in a specific entity reference widget for
+ * and a specific paragraphs type.
+ *
+ * @param array $subform
+ * Nested array of form elements for the paragraphs entity subform in the
+ * widget.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form. The arguments that
+ * \Drupal::formBuilder()->getForm() was originally called with are available
+ * in the array $form_state->getBuildInfo()['args'].
+ * @param int $delta
+ * The order of this item in the array of sub-elements (0, 1, 2, etc.).
+ *
+ * @see hook_form_paragraphs_subform_alter()
+ * @see hook_form_paragraphs_subform_TYPE_alter()
+ * @see hook_form_paragraphs_subform_WIDGET_alter()
+ */
+function hook_form_paragraphs_subform_WIDGET_TYPE_alter(array &$subform, \Drupal\Core\Form\FormStateInterface $form_state, $delta) {
+ $paragraph = $form_state->get('paragraph');
+}
+
+/**
* @} End of "addtogroup hooks".
*/
diff --git a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
index 66697ad..57fcb5b 100644
--- a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
+++ b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
@@ -665,6 +665,14 @@ class InlineParagraphsWidget extends WidgetBase {
}
}
}
+ $form_state->set('paragraph', $paragraphs_entity);
+ $hooks = [
+ 'form_paragraphs_subform',
+ 'form_paragraphs_subform_' . $paragraphs_entity->getParagraphType()->id(),
+ 'form_paragraphs_subform_classic',
+ 'form_paragraphs_subform_classic_' . $paragraphs_entity->getParagraphType()->id(),
+ ];
+ \Drupal::ModuleHandler()->alter($hooks, $element['subform'], $form_state, $delta);
}
elseif ($item_mode == 'preview') {
$element['subform'] = array();
diff --git a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
index 4b37b24..24dbff7 100644
--- a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
+++ b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
@@ -709,6 +709,14 @@ class ParagraphsWidget extends WidgetBase {
}
}
}
+ $form_state->set('paragraph', $paragraphs_entity);
+ $hooks = [
+ 'form_paragraphs_subform',
+ 'form_paragraphs_subform_' . $paragraphs_entity->getParagraphType()->id(),
+ 'form_paragraphs_subform_experimental',
+ 'form_paragraphs_subform_experimental_' . $paragraphs_entity->getParagraphType()->id(),
+ ];
+ \Drupal::ModuleHandler()->alter($hooks, $element['subform'], $form_state, $delta);
}
elseif ($item_mode == 'closed') {
$element['subform'] = [];
diff --git a/tests/modules/paragraphs_test/paragraphs_test.module b/tests/modules/paragraphs_test/paragraphs_test.module
index acec74d..21545d8 100644
--- a/tests/modules/paragraphs_test/paragraphs_test.module
+++ b/tests/modules/paragraphs_test/paragraphs_test.module
@@ -6,6 +6,7 @@
*/
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Render\Element;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\paragraphs\Plugin\Field\FieldWidget\ParagraphsWidget;
@@ -41,3 +42,55 @@ function paragraphs_test_paragraph_view(array &$build, ParagraphInterface $entit
$parent_field_name = $entity->get('parent_field_name')->value;
drupal_set_message("Parent: $parent_type/$parent_id/$parent_field_name", 'status', TRUE);
}
+
+/**
+ * Implements hook_form_paragraphs_subform_alter().
+ */
+function paragraphs_test_form_paragraphs_subform_alter(array &$subform) {
+ foreach (Element::children($subform) as $key) {
+ // Check if container suffix empty. Needs to avoid join warning.
+ if (empty($subform[$key]['#suffix'])) {
+ $subform[$key]['#suffix'] = '';
+ }
+ $subform[$key]['#suffix'] .= 'Here is element added by hook_form_paragraphs_subform_alter(). Key - ' . $key . '
';
+ }
+}
+
+/**
+ * Implements hook_form_paragraphs_subform_TYPE_alter().
+ */
+function paragraphs_test_form_paragraphs_subform_text_paragraph_alter(array &$subform) {
+ foreach (Element::children($subform) as $key) {
+ // Check if container suffix empty. Needs to avoid join warning.
+ if (empty($subform[$key]['#suffix'])) {
+ $subform[$key]['#suffix'] = '';
+ }
+ $subform[$key]['#suffix'] .= 'Here is subform for paragraph type `text_paragraph` added by hook_form_paragraphs_subform_TYPE_alter()
';
+ }
+}
+
+/**
+ * Implements hook_form_paragraphs_subform_WIDGET_alter().
+ */
+function paragraphs_test_form_paragraphs_subform_experimental_alter(array &$subform) {
+ foreach (Element::children($subform) as $key) {
+ // Check if container suffix empty. Needs to avoid join warning.
+ if (empty($subform[$key]['#suffix'])) {
+ $subform[$key]['#suffix'] = '';
+ }
+ $subform[$key]['#suffix'] .= 'Here is subform for experimental paragraph widget added by hook_form_paragraphs_subform_WIDGET_alter()
';
+ }
+}
+
+/**
+ * Implements hook_form_paragraphs_subform_WIDGET_TYPE_alter().
+ */
+function paragraphs_test_form_paragraphs_subform_experimental_text_paragraph_alter(array &$subform) {
+ foreach (Element::children($subform) as $key) {
+ // Check if container suffix empty. Needs to avoid join warning.
+ if (empty($subform[$key]['#suffix'])) {
+ $subform[$key]['#suffix'] = '';
+ }
+ $subform[$key]['#suffix'] .= 'Here is subform for experimental paragraph widget type `text_paragraph` added by hook_form_paragraphs_subform_WIDGET_TYPE_alter()
';
+ }
+}
diff --git a/tests/src/Functional/ParagraphsExperimentalSubformTest.php b/tests/src/Functional/ParagraphsExperimentalSubformTest.php
new file mode 100644
index 0000000..4e06a87
--- /dev/null
+++ b/tests/src/Functional/ParagraphsExperimentalSubformTest.php
@@ -0,0 +1,115 @@
+addParagraphedContentType('paragraphed_test');
+
+ $permissions = [
+ 'administer content types',
+ 'administer node fields',
+ 'administer paragraphs types',
+ 'administer node form display',
+ 'administer paragraph fields',
+ 'administer paragraph form display',
+ 'create paragraphed_test content',
+ 'edit any paragraphed_test content',
+ ];
+ $this->loginAsAdmin($permissions, TRUE);
+
+ // Add new Paragraph types.
+ $this->addParagraphsType('text_paragraph');
+ $this->addFieldtoParagraphType('text_paragraph', 'field_text', 'text_long');
+
+ $this->addParagraphsType('another_paragraph');
+ $this->addFieldtoParagraphType('another_paragraph', 'field_title', 'string');
+
+ // Test for the node add form.
+ // New paragraphs.
+ $this->drupalGet('node/add/paragraphed_test');
+ $this->getSession()->getPage()->findButton('field_paragraphs_text_paragraph_add_more')->press();
+ $this->getSession()->getPage()->findButton('field_paragraphs_another_paragraph_add_more')->press();
+
+ // Checks that text added by hook_form_paragraphs_subform_alter() exists.
+ $this->assertSession()->pageTextContains('Here is element added by hook_form_paragraphs_subform_alter(). Key - field_text');
+ $this->assertSession()->pageTextContains('Here is element added by hook_form_paragraphs_subform_alter(). Key - field_title');
+
+ // Checks text added by hook_form_paragraphs_subform_TYPE_alter() exists.
+ $this->assertSession()->pageTextContains('Here is subform for paragraph type `text_paragraph` added by hook_form_paragraphs_subform_TYPE_alter()');
+
+ // Checks text added by hook_form_paragraphs_subform_WIDGET_alter() exists.
+ $this->assertSession()->pageTextContains('Here is subform for experimental paragraph widget added by hook_form_paragraphs_subform_WIDGET_alter()');
+
+ // Checks text added by
+ // hook_form_paragraphs_subform_WIDGET_TYPE_alter() exists.
+ $this->assertSession()->pageTextContains('Here is subform for experimental paragraph widget type `text_paragraph` added by hook_form_paragraphs_subform_WIDGET_TYPE_alter()');
+
+ // Check for node editing with closed paragraphs.
+ $edit = [
+ 'title[0][value]' => 'Paragraphs Subform test node',
+ 'field_paragraphs[0][subform][field_text][0][value]' => 'Text paragraph',
+ 'field_paragraphs[1][subform][field_title][0][value]' => 'Title paragraph',
+ ];
+ $this->drupalPostForm(NULL, $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle('Paragraphs Subform test node');
+
+ // Set the settings to "Closed" to check that subforms are clean.
+ $settings = [
+ 'edit_mode' => 'closed',
+ 'closed_mode' => 'summary',
+ 'autocollapse' => 'none',
+ ];
+ $this->setParagraphsWidgetSettings('paragraphed_test', 'field_paragraphs', $settings);
+
+ $this->drupalGet('node/' . $node->id() . '/edit');
+
+ // Check if all subforms are closed.
+ $this->assertSession()->pageTextNotContains('Here is element added by');
+ $this->assertSession()->pageTextNotContains('Here is subform for experimental paragraph widget');
+
+ // Open first `text_paragraph`.
+ $this->getSession()->getPage()->findButton('field_paragraphs_0_edit')->press();
+
+ // It should contains all texts except 'Here is element added by
+ // hook_form_paragraphs_subform_alter(). Key - field_title'.
+ $this->assertSession()->pageTextContains('Here is element added by hook_form_paragraphs_subform_alter(). Key - field_text');
+ $this->assertSession()->pageTextContains('Here is subform for paragraph type `text_paragraph` added by hook_form_paragraphs_subform_TYPE_alter()');
+ $this->assertSession()->pageTextContains('Here is subform for experimental paragraph widget added by hook_form_paragraphs_subform_WIDGET_alter()');
+ $this->assertSession()->pageTextContains('Here is subform for experimental paragraph widget type `text_paragraph` added by hook_form_paragraphs_subform_WIDGET_TYPE_alter()');
+ $this->assertSession()->pageTextNotContains('Here is element added by hook_form_paragraphs_subform_alter(). Key - field_title');
+
+ }
+
+}