|
@@ -0,0 +1,810 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+/**
|
|
|
+ * @file
|
|
|
+ * Allows administrators to attach custom fields to fieldable types.
|
|
|
+ */
|
|
|
+
|
|
|
+use Drupal\Core\Entity\ContentEntityFormInterface;
|
|
|
+use Drupal\Core\Entity\Display\EntityDisplayInterface;
|
|
|
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
|
|
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
|
|
+use Drupal\Core\Entity\EntityInterface;
|
|
|
+use Drupal\Core\Form\ConfirmFormInterface;
|
|
|
+use Drupal\Core\Form\FormStateInterface;
|
|
|
+use Drupal\Core\Render\Element;
|
|
|
+
|
|
|
+require_once __DIR__ . '/includes/helpers.inc';
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_theme_registry_alter().
|
|
|
+ */
|
|
|
+function field_group_theme_registry_alter(&$theme_registry) {
|
|
|
+
|
|
|
+ // Inject field_group_build_entity_groups in all entity theming functions.
|
|
|
+ $entity_info = Drupal::entityTypeManager()->getDefinitions();
|
|
|
+ $entity_types = array();
|
|
|
+ foreach ($entity_info as $entity_type_id => $entity_type) {
|
|
|
+ if ($route_name = $entity_type->get('field_ui_base_route')) {
|
|
|
+ $entity_types[] = $entity_type_id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($theme_registry as $theme_hook => $info) {
|
|
|
+ if (in_array($theme_hook, $entity_types) || (!empty($info['base hook']) && in_array($info['base hook'], $entity_types))) {
|
|
|
+ $theme_registry[$theme_hook]['preprocess functions'][] = 'field_group_build_entity_groups';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ECK does not use the eck as theme function.
|
|
|
+ if (isset($theme_registry['eck_entity'])) {
|
|
|
+ $theme_registry['eck_entity']['preprocess functions'][] = 'field_group_build_entity_groups';
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_theme().
|
|
|
+ */
|
|
|
+function field_group_theme() {
|
|
|
+ return array(
|
|
|
+ 'horizontal_tabs' => array(
|
|
|
+ 'render element' => 'element',
|
|
|
+ 'template' => 'horizontal-tabs',
|
|
|
+ 'file' => 'templates/theme.inc',
|
|
|
+ ),
|
|
|
+ 'field_group_accordion_item' => array(
|
|
|
+ 'render element' => 'element',
|
|
|
+ 'template' => 'field-group-accordion-item',
|
|
|
+ 'file' => 'templates/theme.inc',
|
|
|
+ ),
|
|
|
+ 'field_group_accordion' => array(
|
|
|
+ 'render element' => 'element',
|
|
|
+ 'template' => 'field-group-accordion',
|
|
|
+ 'file' => 'templates/theme.inc',
|
|
|
+ ),
|
|
|
+ 'field_group_html_element' => array(
|
|
|
+ 'render element' => 'element',
|
|
|
+ 'template' => 'field-group-html-element',
|
|
|
+ 'file' => 'templates/theme.inc',
|
|
|
+ ),
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_theme_suggestions_alter().
|
|
|
+ *
|
|
|
+ * @param array $suggestions
|
|
|
+ * @param array $variables
|
|
|
+ * @param $hook
|
|
|
+ */
|
|
|
+function field_group_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
|
|
|
+ switch ($hook) {
|
|
|
+ case 'horizontal_tabs':
|
|
|
+ case 'field_group_accordion_item':
|
|
|
+ case 'field_group_accordion':
|
|
|
+ case 'field_group_html_element':
|
|
|
+ $element = $variables['element'];
|
|
|
+
|
|
|
+ $name = $element['#group_name'];
|
|
|
+ $entity_type = $element['#entity_type'];
|
|
|
+ $bundle = $element['#bundle'];
|
|
|
+
|
|
|
+ $wrapper = '';
|
|
|
+ if (isset($element['#wrapper_element'])) {
|
|
|
+ $wrapper = $element['#wrapper_element'];
|
|
|
+ $suggestions[] = $hook . '__' . $wrapper;
|
|
|
+ }
|
|
|
+
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type;
|
|
|
+ $suggestions[] = $hook . '__' . $bundle;
|
|
|
+ $suggestions[] = $hook . '__' . $name;
|
|
|
+
|
|
|
+ if ($wrapper) {
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type . '__' . $wrapper;
|
|
|
+ }
|
|
|
+
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle;
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type . '__' . $name;
|
|
|
+
|
|
|
+ if ($wrapper) {
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $wrapper;
|
|
|
+ }
|
|
|
+ $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $name;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_form_FORM_ID_alter().
|
|
|
+ * Using hook_form_field_ui_form_display_overview_form_alter.
|
|
|
+ */
|
|
|
+function field_group_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
|
|
|
+ $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
|
|
|
+ field_group_field_ui_display_form_alter($form, $form_state);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_form_FORM_ID_alter().
|
|
|
+ * Using hook_form_field_ui_display_overview_form_alter.
|
|
|
+ */
|
|
|
+function field_group_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
|
|
|
+ $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
|
|
|
+ field_group_field_ui_display_form_alter($form, $form_state);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_field_info_max_weight().
|
|
|
+ */
|
|
|
+function field_group_field_info_max_weight($entity_type, $bundle, $context, $context_mode) {
|
|
|
+
|
|
|
+ $groups = field_group_info_groups($entity_type, $bundle, $context, $context_mode);
|
|
|
+
|
|
|
+ $weights = array();
|
|
|
+ foreach ($groups as $group) {
|
|
|
+ $weights[] = $group->weight;
|
|
|
+ }
|
|
|
+ return $weights ? max($weights) : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_form_alter().
|
|
|
+ */
|
|
|
+function field_group_form_alter(array &$form, FormStateInterface $form_state) {
|
|
|
+
|
|
|
+ $form_object = $form_state->getFormObject();
|
|
|
+ if ($form_object instanceof ContentEntityFormInterface && !$form_object instanceof ConfirmFormInterface) {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @var EntityFormDisplayInterface $form_display
|
|
|
+ */
|
|
|
+ $storage = $form_state->getStorage();
|
|
|
+ if (!empty($storage['form_display'])) {
|
|
|
+ $form_display = $storage['form_display'];
|
|
|
+ $entity = $form_object->getEntity();
|
|
|
+
|
|
|
+ $context = array(
|
|
|
+ 'entity_type' => $entity->getEntityTypeId(),
|
|
|
+ 'bundle' => $entity->bundle(),
|
|
|
+ 'entity' => $entity,
|
|
|
+ 'context' => 'form',
|
|
|
+ 'display_context' => 'form',
|
|
|
+ 'mode' => $form_display->getMode(),
|
|
|
+ );
|
|
|
+
|
|
|
+ field_group_attach_groups($form, $context);
|
|
|
+ $form['#pre_render'][] = 'field_group_form_pre_render';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_inline_entity_form_entity_form_alter().
|
|
|
+ */
|
|
|
+function field_group_inline_entity_form_entity_form_alter(&$entity_form, FormStateInterface $form_state) {
|
|
|
+
|
|
|
+ // Attach the fieldgroups to current entity form.
|
|
|
+ $context = [
|
|
|
+ 'entity_type' => $entity_form['#entity']->getEntityTypeId(),
|
|
|
+ 'bundle' => $entity_form['#entity']->bundle(),
|
|
|
+ 'entity' => $entity_form['#entity'],
|
|
|
+ 'display_context' => 'form',
|
|
|
+ 'mode' => isset($entity_form['#form_mode']) ? $entity_form['#form_mode'] : 'default',
|
|
|
+ ];
|
|
|
+
|
|
|
+ field_group_attach_groups($entity_form, $context);
|
|
|
+ $entity_form['#pre_render'][] = 'field_group_form_pre_render';
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_entity_view_alter().
|
|
|
+ */
|
|
|
+function field_group_entity_view_alter(&$build, EntityInterface $entity, EntityDisplayInterface $display) {
|
|
|
+
|
|
|
+ $context = array(
|
|
|
+ 'entity_type' => $display->getTargetEntityTypeId(),
|
|
|
+ 'bundle' => $entity->bundle(),
|
|
|
+ 'entity' => $entity,
|
|
|
+ 'display_context' => 'view',
|
|
|
+ 'mode' => $display->getMode(),
|
|
|
+ );
|
|
|
+
|
|
|
+ field_group_attach_groups($build, $context);
|
|
|
+
|
|
|
+ // If no theme hook, we have no theme hook to preprocess.
|
|
|
+ // Add a prerender.
|
|
|
+ if (empty($build['#theme'])) {
|
|
|
+
|
|
|
+ $ds_enabled = false;
|
|
|
+ if (Drupal::moduleHandler()->moduleExists('ds')) {
|
|
|
+ // Check if DS is enabled for this display.
|
|
|
+ if ($display->getThirdPartySetting('ds', 'layout') && !Drupal\ds\Ds::isDisabled()) {
|
|
|
+ $ds_enabled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // If DS is enabled, no pre render is needed (DS adds fieldgroup preprocessing).
|
|
|
+ if (!$ds_enabled) {
|
|
|
+ $build['#pre_render'][] = 'field_group_entity_view_pre_render';
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Pre render callback for rendering groups.
|
|
|
+ * @see field_group_field_attach_form
|
|
|
+ * @param $element Form that is being rendered.
|
|
|
+ */
|
|
|
+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');
|
|
|
+ }
|
|
|
+ return $element;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Pre render callback for rendering groups on entities without theme hook.
|
|
|
+ * @param $element
|
|
|
+ * Entity being rendered.
|
|
|
+ */
|
|
|
+function field_group_entity_view_pre_render($element) {
|
|
|
+ field_group_build_entity_groups($element, 'view');
|
|
|
+ return $element;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_field_group_pre_render().
|
|
|
+ *
|
|
|
+ * @param Array $element
|
|
|
+ * Group beïng rendered.
|
|
|
+ * @param Object $group
|
|
|
+ * The Field group info.
|
|
|
+ * @param $rendering_object
|
|
|
+ * The entity / form beïng rendered
|
|
|
+ */
|
|
|
+function field_group_field_group_pre_render(&$element, &$group, &$rendering_object) {
|
|
|
+
|
|
|
+ // Add all field_group format types to the js settings.
|
|
|
+ $element['#attached']['drupalSettings']['field_group'] = array(
|
|
|
+ $group->format_type => [
|
|
|
+ 'mode' => $group->mode,
|
|
|
+ 'context' => $group->context,
|
|
|
+ 'settings' => $group->format_settings,
|
|
|
+ ],
|
|
|
+ );
|
|
|
+
|
|
|
+ $element['#weight'] = $group->weight;
|
|
|
+
|
|
|
+ // Call the pre render function for the format type.
|
|
|
+ $manager = Drupal::service('plugin.manager.field_group.formatters');
|
|
|
+ $plugin = $manager->getInstance(array(
|
|
|
+ 'format_type' => $group->format_type,
|
|
|
+ 'configuration' => array('label' => $group->label, 'settings' => $group->format_settings),
|
|
|
+ 'group' => $group,
|
|
|
+ ));
|
|
|
+ $plugin->preRender($element, $rendering_object);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Implements hook_field_group_build_pre_render_alter().
|
|
|
+ * @param Array $elements by address.
|
|
|
+ */
|
|
|
+function field_group_field_group_build_pre_render_alter(& $element) {
|
|
|
+
|
|
|
+ // Someone is doing a node view, in a node view. Reset content.
|
|
|
+ if (isset($element['#node']->content) && count($element['#node']->content) > 0) {
|
|
|
+ $element['#node']->content = array();
|
|
|
+ }
|
|
|
+
|
|
|
+ $display = isset($element['#view_mode']);
|
|
|
+ $groups = array_keys($element['#fieldgroups']);
|
|
|
+
|
|
|
+ // Dish the fieldgroups with no fields for non-forms.
|
|
|
+ if ($display) {
|
|
|
+ field_group_remove_empty_display_groups($element, $groups);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Fix the problem on forms with additional settings.
|
|
|
+ field_group_remove_empty_form_groups('form', $element, $groups, $element['#fieldgroups'], $element['#entity_type']);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Attach groups to the (form) build.
|
|
|
+ *
|
|
|
+ * @param Array $element
|
|
|
+ * The part of the form.
|
|
|
+ * @param Array $context
|
|
|
+ * The contextual information.
|
|
|
+ */
|
|
|
+function field_group_attach_groups(&$element, $context) {
|
|
|
+
|
|
|
+ $entity_type = $context['entity_type'];
|
|
|
+ $bundle = $context['bundle'];
|
|
|
+ $mode = $context['mode'];
|
|
|
+ $display_context = $context['display_context'];
|
|
|
+
|
|
|
+ $element['#fieldgroups'] = field_group_info_groups($entity_type, $bundle, $display_context, $mode);
|
|
|
+
|
|
|
+ // Create a lookup array.
|
|
|
+ $group_children = array();
|
|
|
+ foreach ($element['#fieldgroups'] as $group_name => $group) {
|
|
|
+ foreach ($group->children as $child) {
|
|
|
+ $group_children[$child] = $group_name;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $element['#group_children'] = $group_children;
|
|
|
+ $element['#entity_type'] = $entity_type;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Preprocess/ Pre-render callback.
|
|
|
+ *
|
|
|
+ * @see field_group_form_pre_render()
|
|
|
+ * @see field_group_theme_registry_alter
|
|
|
+ * @see field_group_fields_nest()
|
|
|
+ * @param $vars preprocess vars or form element
|
|
|
+ * @param $context The display context (entity type, form or view)
|
|
|
+ * @return $element Array with re-arranged fields in groups.
|
|
|
+ */
|
|
|
+function field_group_build_entity_groups(&$vars, $context = 'view') {
|
|
|
+
|
|
|
+ if ($context == 'form') {
|
|
|
+ $element = &$vars;
|
|
|
+ $nest_vars = NULL;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (isset($vars['elements'])) {
|
|
|
+ $element = &$vars['elements'];
|
|
|
+ }
|
|
|
+ elseif (isset($vars['content'])) {
|
|
|
+ $element = &$vars['content'];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if ($context === 'eck_entity') {
|
|
|
+ $element = &$vars['entity'];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $element = &$vars;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $nest_vars = &$vars;
|
|
|
+ }
|
|
|
+
|
|
|
+ // No groups on the entity.
|
|
|
+ if (empty($element['#fieldgroups'])) {
|
|
|
+ return $element;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Nest the fields in the corresponding field groups.
|
|
|
+ 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);
|
|
|
+
|
|
|
+ // Return the element on forms.
|
|
|
+ if ($context == 'form') {
|
|
|
+ return $element;
|
|
|
+ }
|
|
|
+
|
|
|
+ // No groups on the entity. Prerender removed empty field groups.
|
|
|
+ if (empty($element['#fieldgroups'])) {
|
|
|
+ return $element;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Put groups inside content if we are rendering an entity_view.
|
|
|
+ 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];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Recursive function to nest 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.
|
|
|
+ * The current group element in the loop is passed recursively so we can
|
|
|
+ * stash fields and groups in it while we go deeper in the array.
|
|
|
+ * @param Array $element
|
|
|
+ * The current element to analyse for grouping.
|
|
|
+ * @param Array $vars
|
|
|
+ * Rendering vars from the entity being viewed.
|
|
|
+ * @param Array $context
|
|
|
+ * The display context (entity type, form or view).
|
|
|
+ */
|
|
|
+function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) {
|
|
|
+
|
|
|
+ // Create all groups and keep a flat list of references to these groups.
|
|
|
+ $group_references = array();
|
|
|
+ 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] = array();
|
|
|
+ $group_references[$group_name] = &$element[$group_name];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Loop through all form 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 = array();
|
|
|
+ foreach (Element::children($element) as $child_name) {
|
|
|
+ $element_clone[$child_name] = $element[$child_name];
|
|
|
+ // If this element is in a group, create the placeholder element.
|
|
|
+ if (isset($element['#group_children'][$child_name])) {
|
|
|
+ $element_clone[$element['#group_children'][$child_name]] = array();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $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 => $parent_name) {
|
|
|
+
|
|
|
+ // Entity being viewed
|
|
|
+ if ($vars) {
|
|
|
+ // 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][$child_name])) {
|
|
|
+ $group_references[$parent_name][$child_name] = $vars[$key][$child_name];
|
|
|
+ unset($vars[$key][$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[$parent_name][$child_name] = &$element[$child_name];
|
|
|
+ unset($element[$child_name]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Form being viewed
|
|
|
+ else {
|
|
|
+ // Block denied fields (#access) before they are put in groups.
|
|
|
+ // Fields (not groups) that don't have children (like field_permissions) are removed
|
|
|
+ // in field_group_field_group_build_pre_render_alter.
|
|
|
+ if (isset($element[$child_name]) && (!isset($element[$child_name]['#access']) || $element[$child_name]['#access'])) {
|
|
|
+ // 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];
|
|
|
+ $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The child has been copied to its parent: remove it from the root element.
|
|
|
+ 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()
|
|
|
+ *
|
|
|
+ * @param $element
|
|
|
+ * Render array of group element that needs to be created.
|
|
|
+ * @param $group
|
|
|
+ * Object with the group information.
|
|
|
+ * @param $rendering_object
|
|
|
+ * The entity / form beïng rendered.
|
|
|
+ */
|
|
|
+function field_group_pre_render(& $element, $group, & $rendering_object) {
|
|
|
+
|
|
|
+ // Only run the pre_render function if the group has elements.
|
|
|
+ // $group->group_name
|
|
|
+ if ($element == array()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Let modules define their wrapping element.
|
|
|
+ // Note that the group element has no properties, only elements.
|
|
|
+ foreach (Drupal::moduleHandler()->getImplementations('field_group_pre_render') as $module) {
|
|
|
+ // The intention here is to have the opportunity to alter the
|
|
|
+ // elements, as defined in hook_field_group_formatter_info.
|
|
|
+ // Note, implement $element by reference!
|
|
|
+ $function = $module . '_field_group_pre_render';
|
|
|
+ $function($element, $group, $rendering_object);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Allow others to alter the pre_render.
|
|
|
+ Drupal::moduleHandler()->alter('field_group_pre_render', $element, $group, $rendering_object);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Provides the content element key for a display context.
|
|
|
+ *
|
|
|
+ * This allows entity modules to specify their content element for field group
|
|
|
+ * support, or other modules to add entity module support.
|
|
|
+ *
|
|
|
+ * @param $context
|
|
|
+ * The display context (entity type, form or view).
|
|
|
+ */
|
|
|
+function field_group_get_content_element_key($context = 'default') {
|
|
|
+ $keys = &drupal_static('field_group_content_elements');
|
|
|
+ if (!isset($keys)) {
|
|
|
+ $keys['default'] = 'content';
|
|
|
+ // Allow other modules to alter the array.
|
|
|
+ Drupal::moduleHandler()->alter('field_group_content_element_keys', $keys);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if we have a specific content element key for this entity type.
|
|
|
+ $key = $keys['default'];
|
|
|
+ if (isset($keys[$context])) {
|
|
|
+ $key = $keys[$context];
|
|
|
+ }
|
|
|
+ return $key;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Saves a group definition.
|
|
|
+ *
|
|
|
+ * This function is called by ctools export when calls are made through
|
|
|
+ * ctools_export_crud_save(). It's also used as an api method to add groups to a
|
|
|
+ * display.
|
|
|
+ *
|
|
|
+ * @param \stdClass $group
|
|
|
+ * A group definition.
|
|
|
+ * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
|
|
|
+ * The display to update if known.
|
|
|
+ *
|
|
|
+ * @return \Drupal\Core\Entity\Display\EntityDisplayInterface|NULL
|
|
|
+ * The updated entity display.
|
|
|
+ */
|
|
|
+function field_group_group_save($group, $display = NULL) {
|
|
|
+ if ($display === NULL) {
|
|
|
+ if ($group->context == 'form') {
|
|
|
+ $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
|
|
|
+ }
|
|
|
+ elseif ($group->context == 'view') {
|
|
|
+ $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // If no display was found. It doesn't exist yet, create it.
|
|
|
+ if (!isset($display)) {
|
|
|
+ if ($group->context == 'form') {
|
|
|
+ $display = EntityFormDisplay::create(array(
|
|
|
+ 'targetEntityType' => $group->entity_type,
|
|
|
+ 'bundle' => $group->bundle,
|
|
|
+ 'mode' => $group->mode,
|
|
|
+ ))->setStatus(TRUE);
|
|
|
+ }
|
|
|
+ elseif ($group->context == 'view') {
|
|
|
+ $display = EntityViewDisplay::create(array(
|
|
|
+ 'targetEntityType' => $group->entity_type,
|
|
|
+ 'bundle' => $group->bundle,
|
|
|
+ 'mode' => $group->mode,
|
|
|
+ ))->setStatus(TRUE);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($display)) {
|
|
|
+ $data = (array) $group;
|
|
|
+ unset($data['group_name'], $data['entity_type'], $data['bundle'], $data['mode'], $data['form'], $data['context']);
|
|
|
+ $display->setThirdPartySetting('field_group', $group->group_name, $data);
|
|
|
+ $display->save();
|
|
|
+ }
|
|
|
+
|
|
|
+ return $display;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Delete a field group.
|
|
|
+ *
|
|
|
+ * @param $group
|
|
|
+ * A group definition.
|
|
|
+ */
|
|
|
+function field_group_group_delete($group) {
|
|
|
+ if ($group->context == 'form') {
|
|
|
+ $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
|
|
|
+ }
|
|
|
+ elseif ($group->context == 'view') {
|
|
|
+ $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @var $display \Drupal\Core\Entity\Display\EntityDisplayInterface
|
|
|
+ */
|
|
|
+ if (isset($display)) {
|
|
|
+ $display->unsetThirdPartySetting('field_group', $group->group_name);
|
|
|
+ $display->save();
|
|
|
+ }
|
|
|
+
|
|
|
+ Drupal::moduleHandler()->invokeAll('field_group_delete_field_group', array($group));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Get all groups.
|
|
|
+ *
|
|
|
+ * @param $entity_type
|
|
|
+ * The name of the entity.
|
|
|
+ * @param $bundle
|
|
|
+ * The name of the bundle.
|
|
|
+ * @param $context
|
|
|
+ * The context of the view mode (form or view)
|
|
|
+ * @param $mode
|
|
|
+ * The view mode.
|
|
|
+ */
|
|
|
+function field_group_info_groups($entity_type, $bundle, $context, $mode) {
|
|
|
+ if ($context == 'form') {
|
|
|
+ $display = EntityFormDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
|
|
|
+ if (!$display) {
|
|
|
+ return array();
|
|
|
+ }
|
|
|
+ $data = $display->getThirdPartySettings('field_group');
|
|
|
+ }
|
|
|
+ if ($context == 'view') {
|
|
|
+ $display = EntityViewDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
|
|
|
+ if (!$display) {
|
|
|
+ return array();
|
|
|
+ }
|
|
|
+ $data = $display->getThirdPartySettings('field_group');
|
|
|
+ }
|
|
|
+ $groups = array();
|
|
|
+ if (isset($data) && is_array($data)) {
|
|
|
+ foreach ($data as $group_name => $definition) {
|
|
|
+ $definition += array(
|
|
|
+ 'group_name' => $group_name,
|
|
|
+ 'entity_type' => $entity_type,
|
|
|
+ 'bundle' => $bundle,
|
|
|
+ 'context' => $context,
|
|
|
+ 'mode' => $mode,
|
|
|
+ );
|
|
|
+ $groups[$group_name] = (object) $definition;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return $groups;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Loads a group definition.
|
|
|
+ *
|
|
|
+ * @param $group_name
|
|
|
+ * The name of the group.
|
|
|
+ * @param $entity_type
|
|
|
+ * The name of the entity.
|
|
|
+ * @param $bundle
|
|
|
+ * The name of the bundle.
|
|
|
+ * @param $context
|
|
|
+ * The context of the view mode (form or view)
|
|
|
+ * @param $mode
|
|
|
+ * The view mode to load.
|
|
|
+ */
|
|
|
+function field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode) {
|
|
|
+ $groups = field_group_info_groups($entity_type, $bundle, $context, $mode);
|
|
|
+ if (isset($groups[$group_name])) {
|
|
|
+ return $groups[$group_name];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Checks if a field_group exists in required context.
|
|
|
+ *
|
|
|
+ * @param String $group_name
|
|
|
+ * The name of the group.
|
|
|
+ * @param String $entity_type
|
|
|
+ * The name of the entity.
|
|
|
+ * @param String $bundle
|
|
|
+ * The bundle for the entity.
|
|
|
+ * @param $context
|
|
|
+ * The context of the view mode (form or view)
|
|
|
+ * @param String $mode
|
|
|
+ * The view mode context the group will be rendered.
|
|
|
+ */
|
|
|
+function field_group_exists($group_name, $entity_type, $bundle, $context, $mode) {
|
|
|
+ return (bool) field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Remove empty groups on forms.
|
|
|
+ *
|
|
|
+ * @param String $parent_name
|
|
|
+ * The name of the element.
|
|
|
+ * @param array $element
|
|
|
+ * The element to check the empty state.
|
|
|
+ * @param array $groups
|
|
|
+ * Array of group objects.
|
|
|
+ */
|
|
|
+function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) {
|
|
|
+
|
|
|
+ $exceptions = array('user__account', 'comment__author');
|
|
|
+
|
|
|
+ $children = Element::children($element);
|
|
|
+
|
|
|
+ $hasChildren = FALSE;
|
|
|
+ if (count($children)) {
|
|
|
+ foreach ($children as $childname) {
|
|
|
+
|
|
|
+ if (in_array($childname, $groups)) {
|
|
|
+ field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity);
|
|
|
+ }
|
|
|
+ $exception = $entity . '__' . $childname;
|
|
|
+ $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || isset($element[$childname]['#markup']) || in_array($exception, $exceptions));
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$hasChildren) {
|
|
|
+
|
|
|
+ // Remove empty elements from the #fieldgroups.
|
|
|
+ if (empty($element) && isset($form_groups[$name]) && !is_array($form_groups[$name])) {
|
|
|
+ foreach ($form_groups as $group_name => $group) {
|
|
|
+ if (isset($group->children)) {
|
|
|
+ $group_children = array_flip($group->children);
|
|
|
+ if (isset($group_children[$name])) {
|
|
|
+ unset($form_groups[$group_name]->children[$group_children[$name]]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $element['#access'] = FALSE;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Remove empty groups on entity display.
|
|
|
+ *
|
|
|
+ * @param array $element
|
|
|
+ * The element to check the empty state.
|
|
|
+ * @param array $groups
|
|
|
+ * Array of group objects.
|
|
|
+ */
|
|
|
+function field_group_remove_empty_display_groups(& $element, $groups) {
|
|
|
+
|
|
|
+ $empty_child = TRUE;
|
|
|
+ $empty_group = TRUE;
|
|
|
+
|
|
|
+ // Loop through the visible children for current element.
|
|
|
+ foreach (Element::getVisibleChildren($element) as $name) {
|
|
|
+
|
|
|
+ // Descend if the child is a group.
|
|
|
+ if (in_array($name, $groups)) {
|
|
|
+ $empty_child = field_group_remove_empty_display_groups($element[$name], $groups);
|
|
|
+ if (!$empty_child) {
|
|
|
+ $empty_group = FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Child is a field or a renderable array and the element is not empty.
|
|
|
+ elseif (!empty($element[$name])) {
|
|
|
+ $clone_element = $element[$name];
|
|
|
+ // Weight parameter can make empty element seen as not empty.
|
|
|
+ unset($clone_element['#weight']);
|
|
|
+ if (!Element::isEmpty($clone_element)) {
|
|
|
+ $empty_group = FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Reset an empty group.
|
|
|
+ if ($empty_group) {
|
|
|
+ $element = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $empty_group;
|
|
|
+
|
|
|
+}
|