diff --git a/field_group.module b/field_group.module index e85c489..7a851e4 100644 --- a/field_group.module +++ b/field_group.module @@ -174,10 +174,9 @@ function field_group_form_alter(array &$form, FormStateInterface $form_state) { ); field_group_attach_groups($form, $context); - $form['#pre_render'][] = 'field_group_form_pre_render'; + $form['#process'][] = 'field_group_form_process'; } } - } /** @@ -195,7 +194,7 @@ function field_group_inline_entity_form_entity_form_alter(&$entity_form, FormSta ]; field_group_attach_groups($entity_form, $context); - $entity_form['#pre_render'][] = 'field_group_form_pre_render'; + $entity_form['#process'][] = 'field_group_form_process'; } /** @@ -233,15 +232,14 @@ function field_group_entity_view_alter(&$build, EntityInterface $entity, EntityD } /** - * Pre render callback for rendering groups. - * @see field_group_field_attach_form - * @param $element Form that is being rendered. + * Process callback for field groups. + * + * @param array $element Form that is being processed. + * + * @return array */ -function field_group_form_pre_render($element) { - if (empty($element['#field_group_form_pre_render'])) { - $element['#field_group_form_pre_render'] = TRUE; - field_group_build_entity_groups($element, 'form'); - } +function field_group_form_process(array &$element) { + field_group_build_entity_groups($element, 'form'); return $element; } @@ -258,7 +256,7 @@ function field_group_entity_view_pre_render($element) { /** * Implements hook_field_group_pre_render(). * - * @param Array $element + * @param array $element * Group beïng rendered. * @param Object $group * The Field group info. @@ -307,8 +305,8 @@ function field_group_field_group_build_pre_render_alter(& $element) { if ($display) { field_group_remove_empty_display_groups($element, $groups); } + // Remove all empty groups on the form. else { - // Fix the problem on forms with additional settings. field_group_remove_empty_form_groups('form', $element, $groups, $element['#fieldgroups'], $element['#entity_type']); } @@ -346,7 +344,7 @@ function field_group_attach_groups(&$element, $context) { /** * Preprocess/ Pre-render callback. * - * @see field_group_form_pre_render() + * @see field_group_form_process() * @see field_group_theme_registry_alter * @see field_group_fields_nest() * @param $vars preprocess vars or form element @@ -384,7 +382,13 @@ function field_group_build_entity_groups(&$vars, $context = 'view') { } // Nest the fields in the corresponding field groups. - field_group_fields_nest($element, $nest_vars, $context); + if ($context !== 'form' && isset($element['_field_layout'])) { + field_group_field_layout_fields_nest($element, $nest_vars, $context); + } + else { + field_group_fields_nest($element, $nest_vars, $context); + } + // Allow others to alter the pre_rendered build. Drupal::moduleHandler()->alter('field_group_build_pre_render', $element); @@ -400,11 +404,21 @@ function field_group_build_entity_groups(&$vars, $context = 'view') { } // Put groups inside content if we are rendering an entity_view. + $render_key = field_group_get_content_element_key($context); foreach ($element['#fieldgroups'] as $group) { if (!empty($element[$group->group_name])) { - $key = field_group_get_content_element_key($context); - if (isset($vars[$key])) { - $vars[$key][$group->group_name] = $element[$group->group_name]; + + if (isset($vars[$render_key])) { + + // Field layout enabled? Place it in correct region of the _field_layout key. + if (isset($vars[$render_key]['_field_layout'])) { + $vars[$render_key]['_field_layout'][$group->region][$group->group_name] = $element[$group->group_name]; + } + else { + $vars[$render_key][$group->group_name] = $element[$group->group_name]; + } + + } } } @@ -412,7 +426,7 @@ function field_group_build_entity_groups(&$vars, $context = 'view') { } /** - * Recursive function to nest fields in the field groups. + * Nests all the fields in the field groups. * * This function will take out all the elements in the form and * place them in the correct container element, a fieldgroup. @@ -467,6 +481,7 @@ function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) { $group_references[$parent_name][$child_name] = &$element[$child_name]; unset($element[$child_name]); } + } // Form being viewed else { @@ -477,6 +492,10 @@ function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) { // If this is a group, we have to use a reference to keep the reference // list intact (but if it is a field we don't mind). $group_references[$parent_name][$child_name] = &$element[$child_name]; + + // Remove the #group property, otherwise core will move this element to the field layout region. + unset($group_references[$parent_name][$child_name]['#group']); + $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight; } @@ -495,6 +514,69 @@ function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) { } /** + * Nests all the fields in the field groups for entity display elements managed by field layout. + * @param $element + * @param null $vars + * @param null $context + */ +function field_group_field_layout_fields_nest(&$element, &$vars = NULL, $context = NULL) { + + // Create all groups and keep a flat list of references to these groups. + $group_references = []; + foreach ($element['#fieldgroups'] as $group_name => $group) { + // Construct own weight, as some fields (for example preprocess fields) don't have weight set. + $element[$group_name] = []; + $group_references[$group_name] = &$element[$group_name]; + } + + // Loop through all children looking for those that are supposed to be + // in groups, and insert placeholder element for the new group field in the + // correct location within the form structure. + $element_clone = []; + foreach (Element::children($element['_field_layout']) as $region_name) { + + foreach (Element::children($element['_field_layout'][$region_name]) as $child_name) { + $element_clone['_field_layout'][$region_name][$child_name] = $element['_field_layout'][$region_name][$child_name]; + // If this element is in a group, create the placeholder element. + if (isset($element['_field_layout'][$region_name]['#group_children'][$child_name])) { + $element_clone['_field_layout'][$region_name][$element['#group_children'][$child_name]] = []; + } + } + + } + + $element = array_merge($element_clone, $element); + + // Move all children to their parents. Use the flat list of references for + // direct access as we don't know where in the root_element hierarchy the + // parent currently is situated. + foreach ($element['#group_children'] as $child_name => $group_name) { + + $region = $element['#fieldgroups'][$group_name]->region; + + // If not a group, check the content variable for empty field. + $key = field_group_get_content_element_key($context); + if (!isset($element['#fieldgroups'][$child_name]) && isset($vars[$key]['_field_layout'][$region][$child_name])) { + $group_references[$group_name][$child_name] = $vars[$key]['_field_layout'][$region][$child_name]; + unset($vars[$key]['_field_layout'][$region][$child_name]); + } + // If this is a group, we have to use a reference to keep the reference + // list intact (but if it is a field we don't mind). + else { + $group_references[$group_name][$child_name] = &$element[$child_name]; + unset($element[$child_name]); + } + + } + + // Bring extra element wrappers to achieve a grouping of fields. + // This will mainly be prefix and suffix altering. + foreach ($element['#fieldgroups'] as $group_name => $group) { + field_group_pre_render($group_references[$group_name], $group, $element); + } +} + +/** * Function to pre render the field group element. * * @see field_group_fields_nest() @@ -719,14 +801,18 @@ function field_group_exists($group_name, $entity_type, $bundle, $context, $mode) /** * Remove empty groups on forms. * - * @param String $parent_name - * The name of the element. + * @param string $name + * The name of current group. * @param array $element * The element to check the empty state. * @param array $groups - * Array of group objects. + * Array with group names. + * @param array $form_groups + * Array with full group objects. + * @param $entity + * Current entity type that is rendered. */ -function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) { +function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity_type) { $exceptions = array('user__account', 'comment__author'); @@ -737,9 +823,9 @@ function field_group_remove_empty_form_groups($name, & $element, $groups, &$form foreach ($children as $childname) { if (in_array($childname, $groups)) { - field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity); + field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity_type); } - $exception = $entity . '__' . $childname; + $exception = $entity_type . '__' . $childname; $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || isset($element[$childname]['#markup']) || in_array($exception, $exceptions)); } diff --git a/src/Element/HtmlElement.php b/src/Element/HtmlElement.php index e58d6e1..c899a93 100644 --- a/src/Element/HtmlElement.php +++ b/src/Element/HtmlElement.php @@ -18,12 +18,16 @@ class HtmlElement extends RenderElement { public function getInfo() { $class = get_class($this); - return array( - '#process' => array( - array($class, 'processHtmlElement'), - ), - '#theme_wrappers' => array('field_group_html_element'), - ); + return [ + '#process' => [ + [$class, 'processGroup'], + [$class, 'processHtmlElement'], + ], + '#pre_render' => [ + [$class, 'preRenderGroup'], + ], + '#theme_wrappers' => ['field_group_html_element'], + ]; } /** diff --git a/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php b/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php index ecbf217..d2a17b5 100644 --- a/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php +++ b/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php @@ -28,7 +28,6 @@ class Fieldset extends FieldGroupFormatterBase { $element += array( '#type' => 'fieldset', '#title' => Html::escape($this->t($this->getLabel())), - '#pre_render' => array(), '#attributes' => array(), ); diff --git a/src/Plugin/field_group/FieldGroupFormatter/Tab.php b/src/Plugin/field_group/FieldGroupFormatter/Tab.php index 7cc8a64..7c81c77 100644 --- a/src/Plugin/field_group/FieldGroupFormatter/Tab.php +++ b/src/Plugin/field_group/FieldGroupFormatter/Tab.php @@ -34,6 +34,7 @@ class Tab extends FieldGroupFormatterBase { '#type' => 'details', '#title' => Html::escape($this->t($this->getLabel())), '#description' => $this->getSetting('description'), + '#group' => $this->group->parent_name, ); if ($this->getSetting('id')) { @@ -46,7 +47,7 @@ class Tab extends FieldGroupFormatterBase { $classes = $this->getClasses(); if (!empty($classes)) { $element += array( - '#attributes' => array('class' => $classes), + '#attributes' => ['class' => $classes], ); } @@ -54,13 +55,6 @@ class Tab extends FieldGroupFormatterBase { $element['#open'] = TRUE; } - // Front-end and back-end on configuration will lead - // to vertical tabs nested in a separate vertical group. - if (!empty($this->group->parent_name)) { - $add['#group'] = $this->group->parent_name; - $add['#parents'] = array($add['#group']); - } - if ($this->getSetting('required_fields')) { $element['#attached']['library'][] = 'field_group/formatter.tabs'; $element['#attached']['library'][] = 'field_group/core'; @@ -77,28 +71,28 @@ class Tab extends FieldGroupFormatterBase { $form = parent::settingsForm(); - $form['formatter'] = array( + $form['formatter'] = [ '#title' => $this->t('Default state'), '#type' => 'select', '#options' => array_combine($this->pluginDefinition['format_types'], $this->pluginDefinition['format_types']), '#default_value' => $this->getSetting('formatter'), '#weight' => -4, - ); + ]; - $form['description'] = array( + $form['description'] = [ '#title' => $this->t('Description'), '#type' => 'textarea', '#default_value' => $this->getSetting('description'), '#weight' => -4, - ); + ]; if ($this->context == 'form') { - $form['required_fields'] = array( + $form['required_fields'] = [ '#type' => 'checkbox', '#title' => $this->t('Mark group as required if it contains required fields.'), '#default_value' => $this->getSetting('required_fields'), '#weight' => 2, - ); + ]; } return $form; @@ -108,10 +102,10 @@ class Tab extends FieldGroupFormatterBase { * {@inheritdoc} */ public static function defaultContextSettings($context) { - $defaults = array( + $defaults = [ 'formatter' => 'closed', 'description' => '', - ) + parent::defaultSettings($context); + ] + parent::defaultSettings($context); if ($context == 'form') { $defaults['required_fields'] = 1; diff --git a/src/Plugin/field_group/FieldGroupFormatter/Tabs.php b/src/Plugin/field_group/FieldGroupFormatter/Tabs.php index 4f49ec3..93029e0 100644 --- a/src/Plugin/field_group/FieldGroupFormatter/Tabs.php +++ b/src/Plugin/field_group/FieldGroupFormatter/Tabs.php @@ -28,6 +28,7 @@ class Tabs extends FieldGroupFormatterBase { * {@inheritdoc} */ public function preRender(&$element, $rendering_object) { + parent::preRender($element, $rendering_object); $element += array( @@ -47,30 +48,42 @@ class Tabs extends FieldGroupFormatterBase { $element['#title'] = Html::escape($this->getLabel()); } - $form_state = new FormState(); - if ($this->getSetting('direction') == 'vertical') { $element += array( '#type' => 'vertical_tabs', '#theme_wrappers' => array('vertical_tabs'), ); - $complete_form = array(); - $element = VerticalTabs::processVerticalTabs($element, $form_state, $complete_form); + + // View is not getting processed anymore. + if ($this->context === 'view') { + $form_state = new FormState(); + $complete_form = array(); + $element = VerticalTabs::processVerticalTabs($element, $form_state, $complete_form); + } + } else { $element += array( '#type' => 'horizontal_tabs', '#theme_wrappers' => array('horizontal_tabs'), ); - $on_form = $this->context == 'form'; - $element = HorizontalTabs::processHorizontalTabs($element, $form_state, $on_form); + + // View is not getting processed anymore. + if ($this->context === 'view') { + $form_state = new FormState(); + $complete_form = array(); + $element = HorizontalTabs::processHorizontalTabs($element, $form_state, $complete_form); + } + } - // Make sure the group has 1 child. This is needed to succeed at form_pre_render_vertical_tabs(). - // Skipping this would force us to move all child groups to this array, making it an un-nestable. - $element['group']['#groups'][$this->group->group_name] = array(0 => array()); - $element['group']['#groups'][$this->group->group_name]['#group_exists'] = TRUE; + if ($this->context === 'view') { + // Make sure the group has 1 child. This is needed to succeed at form_pre_render_vertical_tabs(). + // Skipping this would force us to move all child groups to this array, making it an un-nestable. + $element['group']['#groups'][$this->group->group_name] = [0 => []]; + $element['group']['#groups'][$this->group->group_name]['#group_exists'] = TRUE; + } // Search for a tab that was marked as open. First one wins. foreach (Element::children($element) as $tab_name) {