field_group.module 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. <?php
  2. /**
  3. * @file
  4. * Allows administrators to attach custom fields to fieldable types.
  5. */
  6. use Drupal\Core\Entity\ContentEntityFormInterface;
  7. use Drupal\Core\Entity\Display\EntityDisplayInterface;
  8. use Drupal\Core\Entity\Entity\EntityFormDisplay;
  9. use Drupal\Core\Entity\Entity\EntityViewDisplay;
  10. use Drupal\Core\Entity\EntityInterface;
  11. use Drupal\Core\Form\ConfirmFormInterface;
  12. use Drupal\Core\Form\FormStateInterface;
  13. use Drupal\Core\Render\Element;
  14. require_once __DIR__ . '/includes/helpers.inc';
  15. /**
  16. * Implements hook_theme_registry_alter().
  17. */
  18. function field_group_theme_registry_alter(&$theme_registry) {
  19. // Inject field_group_build_entity_groups in all entity theming functions.
  20. $entity_info = Drupal::entityTypeManager()->getDefinitions();
  21. $entity_types = array();
  22. foreach ($entity_info as $entity_type_id => $entity_type) {
  23. if ($route_name = $entity_type->get('field_ui_base_route')) {
  24. $entity_types[] = $entity_type_id;
  25. }
  26. }
  27. foreach ($theme_registry as $theme_hook => $info) {
  28. if (in_array($theme_hook, $entity_types) || (!empty($info['base hook']) && in_array($info['base hook'], $entity_types))) {
  29. $theme_registry[$theme_hook]['preprocess functions'][] = 'field_group_build_entity_groups';
  30. }
  31. }
  32. // ECK does not use the eck as theme function.
  33. if (isset($theme_registry['eck_entity'])) {
  34. $theme_registry['eck_entity']['preprocess functions'][] = 'field_group_build_entity_groups';
  35. }
  36. }
  37. /**
  38. * Implements hook_theme().
  39. */
  40. function field_group_theme() {
  41. return array(
  42. 'horizontal_tabs' => array(
  43. 'render element' => 'element',
  44. 'template' => 'horizontal-tabs',
  45. 'file' => 'templates/theme.inc',
  46. ),
  47. 'field_group_accordion_item' => array(
  48. 'render element' => 'element',
  49. 'template' => 'field-group-accordion-item',
  50. 'file' => 'templates/theme.inc',
  51. ),
  52. 'field_group_accordion' => array(
  53. 'render element' => 'element',
  54. 'template' => 'field-group-accordion',
  55. 'file' => 'templates/theme.inc',
  56. ),
  57. 'field_group_html_element' => array(
  58. 'render element' => 'element',
  59. 'template' => 'field-group-html-element',
  60. 'file' => 'templates/theme.inc',
  61. ),
  62. );
  63. }
  64. /**
  65. * Implements hook_theme_suggestions_alter().
  66. *
  67. * @param array $suggestions
  68. * @param array $variables
  69. * @param $hook
  70. */
  71. function field_group_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  72. switch ($hook) {
  73. case 'horizontal_tabs':
  74. case 'field_group_accordion_item':
  75. case 'field_group_accordion':
  76. case 'field_group_html_element':
  77. $element = $variables['element'];
  78. $name = $element['#group_name'];
  79. $entity_type = $element['#entity_type'];
  80. $bundle = $element['#bundle'];
  81. $wrapper = '';
  82. if (isset($element['#wrapper_element'])) {
  83. $wrapper = $element['#wrapper_element'];
  84. $suggestions[] = $hook . '__' . $wrapper;
  85. }
  86. $suggestions[] = $hook . '__' . $entity_type;
  87. $suggestions[] = $hook . '__' . $bundle;
  88. $suggestions[] = $hook . '__' . $name;
  89. if ($wrapper) {
  90. $suggestions[] = $hook . '__' . $entity_type . '__' . $wrapper;
  91. }
  92. $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle;
  93. $suggestions[] = $hook . '__' . $entity_type . '__' . $name;
  94. if ($wrapper) {
  95. $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $wrapper;
  96. }
  97. $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $name;
  98. break;
  99. }
  100. }
  101. /**
  102. * Implements hook_form_FORM_ID_alter().
  103. * Using hook_form_field_ui_form_display_overview_form_alter.
  104. */
  105. function field_group_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
  106. $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
  107. field_group_field_ui_display_form_alter($form, $form_state);
  108. }
  109. /**
  110. * Implements hook_form_FORM_ID_alter().
  111. * Using hook_form_field_ui_display_overview_form_alter.
  112. */
  113. function field_group_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
  114. $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
  115. field_group_field_ui_display_form_alter($form, $form_state);
  116. }
  117. /**
  118. * Implements hook_field_info_max_weight().
  119. */
  120. function field_group_field_info_max_weight($entity_type, $bundle, $context, $context_mode) {
  121. $groups = field_group_info_groups($entity_type, $bundle, $context, $context_mode);
  122. $weights = array();
  123. foreach ($groups as $group) {
  124. $weights[] = $group->weight;
  125. }
  126. return $weights ? max($weights) : NULL;
  127. }
  128. /**
  129. * Implements hook_form_alter().
  130. */
  131. function field_group_form_alter(array &$form, FormStateInterface $form_state) {
  132. $form_object = $form_state->getFormObject();
  133. if ($form_object instanceof ContentEntityFormInterface && !$form_object instanceof ConfirmFormInterface) {
  134. /**
  135. * @var EntityFormDisplayInterface $form_display
  136. */
  137. $storage = $form_state->getStorage();
  138. if (!empty($storage['form_display'])) {
  139. $form_display = $storage['form_display'];
  140. $entity = $form_object->getEntity();
  141. $context = array(
  142. 'entity_type' => $entity->getEntityTypeId(),
  143. 'bundle' => $entity->bundle(),
  144. 'entity' => $entity,
  145. 'context' => 'form',
  146. 'display_context' => 'form',
  147. 'mode' => $form_display->getMode(),
  148. );
  149. field_group_attach_groups($form, $context);
  150. $form['#pre_render'][] = 'field_group_form_pre_render';
  151. }
  152. }
  153. }
  154. /**
  155. * Implements hook_inline_entity_form_entity_form_alter().
  156. */
  157. function field_group_inline_entity_form_entity_form_alter(&$entity_form, FormStateInterface $form_state) {
  158. // Attach the fieldgroups to current entity form.
  159. $context = [
  160. 'entity_type' => $entity_form['#entity']->getEntityTypeId(),
  161. 'bundle' => $entity_form['#entity']->bundle(),
  162. 'entity' => $entity_form['#entity'],
  163. 'display_context' => 'form',
  164. 'mode' => isset($entity_form['#form_mode']) ? $entity_form['#form_mode'] : 'default',
  165. ];
  166. field_group_attach_groups($entity_form, $context);
  167. $entity_form['#pre_render'][] = 'field_group_form_pre_render';
  168. }
  169. /**
  170. * Implements hook_entity_view_alter().
  171. */
  172. function field_group_entity_view_alter(&$build, EntityInterface $entity, EntityDisplayInterface $display) {
  173. $context = array(
  174. 'entity_type' => $display->getTargetEntityTypeId(),
  175. 'bundle' => $entity->bundle(),
  176. 'entity' => $entity,
  177. 'display_context' => 'view',
  178. 'mode' => $display->getMode(),
  179. );
  180. field_group_attach_groups($build, $context);
  181. // If no theme hook, we have no theme hook to preprocess.
  182. // Add a prerender.
  183. if (empty($build['#theme'])) {
  184. $ds_enabled = false;
  185. if (Drupal::moduleHandler()->moduleExists('ds')) {
  186. // Check if DS is enabled for this display.
  187. if ($display->getThirdPartySetting('ds', 'layout') && !Drupal\ds\Ds::isDisabled()) {
  188. $ds_enabled = true;
  189. }
  190. }
  191. // If DS is enabled, no pre render is needed (DS adds fieldgroup preprocessing).
  192. if (!$ds_enabled) {
  193. $build['#pre_render'][] = 'field_group_entity_view_pre_render';
  194. }
  195. }
  196. }
  197. /**
  198. * Pre render callback for rendering groups.
  199. * @see field_group_field_attach_form
  200. * @param $element Form that is being rendered.
  201. */
  202. function field_group_form_pre_render($element) {
  203. if (empty($element['#field_group_form_pre_render'])) {
  204. $element['#field_group_form_pre_render'] = TRUE;
  205. field_group_build_entity_groups($element, 'form');
  206. }
  207. return $element;
  208. }
  209. /**
  210. * Pre render callback for rendering groups on entities without theme hook.
  211. * @param $element
  212. * Entity being rendered.
  213. */
  214. function field_group_entity_view_pre_render($element) {
  215. field_group_build_entity_groups($element, 'view');
  216. return $element;
  217. }
  218. /**
  219. * Implements hook_field_group_pre_render().
  220. *
  221. * @param Array $element
  222. * Group beïng rendered.
  223. * @param Object $group
  224. * The Field group info.
  225. * @param $rendering_object
  226. * The entity / form beïng rendered
  227. */
  228. function field_group_field_group_pre_render(&$element, &$group, &$rendering_object) {
  229. // Add all field_group format types to the js settings.
  230. $element['#attached']['drupalSettings']['field_group'] = array(
  231. $group->format_type => [
  232. 'mode' => $group->mode,
  233. 'context' => $group->context,
  234. 'settings' => $group->format_settings,
  235. ],
  236. );
  237. $element['#weight'] = $group->weight;
  238. // Call the pre render function for the format type.
  239. $manager = Drupal::service('plugin.manager.field_group.formatters');
  240. $plugin = $manager->getInstance(array(
  241. 'format_type' => $group->format_type,
  242. 'configuration' => array('label' => $group->label, 'settings' => $group->format_settings),
  243. 'group' => $group,
  244. ));
  245. $plugin->preRender($element, $rendering_object);
  246. }
  247. /**
  248. * Implements hook_field_group_build_pre_render_alter().
  249. * @param Array $elements by address.
  250. */
  251. function field_group_field_group_build_pre_render_alter(& $element) {
  252. // Someone is doing a node view, in a node view. Reset content.
  253. if (isset($element['#node']->content) && count($element['#node']->content) > 0) {
  254. $element['#node']->content = array();
  255. }
  256. $display = isset($element['#view_mode']);
  257. $groups = array_keys($element['#fieldgroups']);
  258. // Dish the fieldgroups with no fields for non-forms.
  259. if ($display) {
  260. field_group_remove_empty_display_groups($element, $groups);
  261. }
  262. else {
  263. // Fix the problem on forms with additional settings.
  264. field_group_remove_empty_form_groups('form', $element, $groups, $element['#fieldgroups'], $element['#entity_type']);
  265. }
  266. }
  267. /**
  268. * Attach groups to the (form) build.
  269. *
  270. * @param Array $element
  271. * The part of the form.
  272. * @param Array $context
  273. * The contextual information.
  274. */
  275. function field_group_attach_groups(&$element, $context) {
  276. $entity_type = $context['entity_type'];
  277. $bundle = $context['bundle'];
  278. $mode = $context['mode'];
  279. $display_context = $context['display_context'];
  280. $element['#fieldgroups'] = field_group_info_groups($entity_type, $bundle, $display_context, $mode);
  281. // Create a lookup array.
  282. $group_children = array();
  283. foreach ($element['#fieldgroups'] as $group_name => $group) {
  284. foreach ($group->children as $child) {
  285. $group_children[$child] = $group_name;
  286. }
  287. }
  288. $element['#group_children'] = $group_children;
  289. $element['#entity_type'] = $entity_type;
  290. }
  291. /**
  292. * Preprocess/ Pre-render callback.
  293. *
  294. * @see field_group_form_pre_render()
  295. * @see field_group_theme_registry_alter
  296. * @see field_group_fields_nest()
  297. * @param $vars preprocess vars or form element
  298. * @param $context The display context (entity type, form or view)
  299. * @return $element Array with re-arranged fields in groups.
  300. */
  301. function field_group_build_entity_groups(&$vars, $context = 'view') {
  302. if ($context == 'form') {
  303. $element = &$vars;
  304. $nest_vars = NULL;
  305. }
  306. else {
  307. if (isset($vars['elements'])) {
  308. $element = &$vars['elements'];
  309. }
  310. elseif (isset($vars['content'])) {
  311. $element = &$vars['content'];
  312. }
  313. else {
  314. if ($context === 'eck_entity') {
  315. $element = &$vars['entity'];
  316. }
  317. else {
  318. $element = &$vars;
  319. }
  320. }
  321. $nest_vars = &$vars;
  322. }
  323. // No groups on the entity.
  324. if (empty($element['#fieldgroups'])) {
  325. return $element;
  326. }
  327. // Nest the fields in the corresponding field groups.
  328. field_group_fields_nest($element, $nest_vars, $context);
  329. // Allow others to alter the pre_rendered build.
  330. Drupal::moduleHandler()->alter('field_group_build_pre_render', $element);
  331. // Return the element on forms.
  332. if ($context == 'form') {
  333. return $element;
  334. }
  335. // No groups on the entity. Prerender removed empty field groups.
  336. if (empty($element['#fieldgroups'])) {
  337. return $element;
  338. }
  339. // Put groups inside content if we are rendering an entity_view.
  340. foreach ($element['#fieldgroups'] as $group) {
  341. if (!empty($element[$group->group_name])) {
  342. $key = field_group_get_content_element_key($context);
  343. if (isset($vars[$key])) {
  344. $vars[$key][$group->group_name] = $element[$group->group_name];
  345. }
  346. }
  347. }
  348. }
  349. /**
  350. * Recursive function to nest fields in the field groups.
  351. *
  352. * This function will take out all the elements in the form and
  353. * place them in the correct container element, a fieldgroup.
  354. * The current group element in the loop is passed recursively so we can
  355. * stash fields and groups in it while we go deeper in the array.
  356. * @param Array $element
  357. * The current element to analyse for grouping.
  358. * @param Array $vars
  359. * Rendering vars from the entity being viewed.
  360. * @param Array $context
  361. * The display context (entity type, form or view).
  362. */
  363. function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) {
  364. // Create all groups and keep a flat list of references to these groups.
  365. $group_references = array();
  366. foreach ($element['#fieldgroups'] as $group_name => $group) {
  367. // Construct own weight, as some fields (for example preprocess fields) don't have weight set.
  368. $element[$group_name] = array();
  369. $group_references[$group_name] = &$element[$group_name];
  370. }
  371. // Loop through all form children looking for those that are supposed to be
  372. // in groups, and insert placeholder element for the new group field in the
  373. // correct location within the form structure.
  374. $element_clone = array();
  375. foreach (Element::children($element) as $child_name) {
  376. $element_clone[$child_name] = $element[$child_name];
  377. // If this element is in a group, create the placeholder element.
  378. if (isset($element['#group_children'][$child_name])) {
  379. $element_clone[$element['#group_children'][$child_name]] = array();
  380. }
  381. }
  382. $element = array_merge($element_clone, $element);
  383. // Move all children to their parents. Use the flat list of references for
  384. // direct access as we don't know where in the root_element hierarchy the
  385. // parent currently is situated.
  386. foreach ($element['#group_children'] as $child_name => $parent_name) {
  387. // Entity being viewed
  388. if ($vars) {
  389. // If not a group, check the content variable for empty field.
  390. $key = field_group_get_content_element_key($context);
  391. if (!isset($element['#fieldgroups'][$child_name]) && isset($vars[$key][$child_name])) {
  392. $group_references[$parent_name][$child_name] = $vars[$key][$child_name];
  393. unset($vars[$key][$child_name]);
  394. }
  395. // If this is a group, we have to use a reference to keep the reference
  396. // list intact (but if it is a field we don't mind).
  397. else {
  398. $group_references[$parent_name][$child_name] = &$element[$child_name];
  399. unset($element[$child_name]);
  400. }
  401. }
  402. // Form being viewed
  403. else {
  404. // Block denied fields (#access) before they are put in groups.
  405. // Fields (not groups) that don't have children (like field_permissions) are removed
  406. // in field_group_field_group_build_pre_render_alter.
  407. if (isset($element[$child_name]) && (!isset($element[$child_name]['#access']) || $element[$child_name]['#access'])) {
  408. // If this is a group, we have to use a reference to keep the reference
  409. // list intact (but if it is a field we don't mind).
  410. $group_references[$parent_name][$child_name] = &$element[$child_name];
  411. $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight;
  412. }
  413. // The child has been copied to its parent: remove it from the root element.
  414. unset($element[$child_name]);
  415. }
  416. }
  417. // Bring extra element wrappers to achieve a grouping of fields.
  418. // This will mainly be prefix and suffix altering.
  419. foreach ($element['#fieldgroups'] as $group_name => $group) {
  420. field_group_pre_render($group_references[$group_name], $group, $element);
  421. }
  422. }
  423. /**
  424. * Function to pre render the field group element.
  425. *
  426. * @see field_group_fields_nest()
  427. *
  428. * @param $element
  429. * Render array of group element that needs to be created.
  430. * @param $group
  431. * Object with the group information.
  432. * @param $rendering_object
  433. * The entity / form beïng rendered.
  434. */
  435. function field_group_pre_render(& $element, $group, & $rendering_object) {
  436. // Only run the pre_render function if the group has elements.
  437. // $group->group_name
  438. if ($element == array()) {
  439. return;
  440. }
  441. // Let modules define their wrapping element.
  442. // Note that the group element has no properties, only elements.
  443. foreach (Drupal::moduleHandler()->getImplementations('field_group_pre_render') as $module) {
  444. // The intention here is to have the opportunity to alter the
  445. // elements, as defined in hook_field_group_formatter_info.
  446. // Note, implement $element by reference!
  447. $function = $module . '_field_group_pre_render';
  448. $function($element, $group, $rendering_object);
  449. }
  450. // Allow others to alter the pre_render.
  451. Drupal::moduleHandler()->alter('field_group_pre_render', $element, $group, $rendering_object);
  452. }
  453. /**
  454. * Provides the content element key for a display context.
  455. *
  456. * This allows entity modules to specify their content element for field group
  457. * support, or other modules to add entity module support.
  458. *
  459. * @param $context
  460. * The display context (entity type, form or view).
  461. */
  462. function field_group_get_content_element_key($context = 'default') {
  463. $keys = &drupal_static('field_group_content_elements');
  464. if (!isset($keys)) {
  465. $keys['default'] = 'content';
  466. // Allow other modules to alter the array.
  467. Drupal::moduleHandler()->alter('field_group_content_element_keys', $keys);
  468. }
  469. // Check if we have a specific content element key for this entity type.
  470. $key = $keys['default'];
  471. if (isset($keys[$context])) {
  472. $key = $keys[$context];
  473. }
  474. return $key;
  475. }
  476. /**
  477. * Saves a group definition.
  478. *
  479. * This function is called by ctools export when calls are made through
  480. * ctools_export_crud_save(). It's also used as an api method to add groups to a
  481. * display.
  482. *
  483. * @param \stdClass $group
  484. * A group definition.
  485. * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
  486. * The display to update if known.
  487. *
  488. * @return \Drupal\Core\Entity\Display\EntityDisplayInterface|NULL
  489. * The updated entity display.
  490. */
  491. function field_group_group_save($group, $display = NULL) {
  492. if ($display === NULL) {
  493. if ($group->context == 'form') {
  494. $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
  495. }
  496. elseif ($group->context == 'view') {
  497. $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
  498. }
  499. }
  500. // If no display was found. It doesn't exist yet, create it.
  501. if (!isset($display)) {
  502. if ($group->context == 'form') {
  503. $display = EntityFormDisplay::create(array(
  504. 'targetEntityType' => $group->entity_type,
  505. 'bundle' => $group->bundle,
  506. 'mode' => $group->mode,
  507. ))->setStatus(TRUE);
  508. }
  509. elseif ($group->context == 'view') {
  510. $display = EntityViewDisplay::create(array(
  511. 'targetEntityType' => $group->entity_type,
  512. 'bundle' => $group->bundle,
  513. 'mode' => $group->mode,
  514. ))->setStatus(TRUE);
  515. }
  516. }
  517. if (isset($display)) {
  518. $data = (array) $group;
  519. unset($data['group_name'], $data['entity_type'], $data['bundle'], $data['mode'], $data['form'], $data['context']);
  520. $display->setThirdPartySetting('field_group', $group->group_name, $data);
  521. $display->save();
  522. }
  523. return $display;
  524. }
  525. /**
  526. * Delete a field group.
  527. *
  528. * @param $group
  529. * A group definition.
  530. */
  531. function field_group_group_delete($group) {
  532. if ($group->context == 'form') {
  533. $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
  534. }
  535. elseif ($group->context == 'view') {
  536. $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
  537. }
  538. /**
  539. * @var $display \Drupal\Core\Entity\Display\EntityDisplayInterface
  540. */
  541. if (isset($display)) {
  542. $display->unsetThirdPartySetting('field_group', $group->group_name);
  543. $display->save();
  544. }
  545. Drupal::moduleHandler()->invokeAll('field_group_delete_field_group', array($group));
  546. }
  547. /**
  548. * Get all groups.
  549. *
  550. * @param $entity_type
  551. * The name of the entity.
  552. * @param $bundle
  553. * The name of the bundle.
  554. * @param $context
  555. * The context of the view mode (form or view)
  556. * @param $mode
  557. * The view mode.
  558. */
  559. function field_group_info_groups($entity_type, $bundle, $context, $mode) {
  560. if ($context == 'form') {
  561. $display = EntityFormDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
  562. if (!$display) {
  563. return array();
  564. }
  565. $data = $display->getThirdPartySettings('field_group');
  566. }
  567. if ($context == 'view') {
  568. $display = EntityViewDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
  569. if (!$display) {
  570. return array();
  571. }
  572. $data = $display->getThirdPartySettings('field_group');
  573. }
  574. $groups = array();
  575. if (isset($data) && is_array($data)) {
  576. foreach ($data as $group_name => $definition) {
  577. $definition += array(
  578. 'group_name' => $group_name,
  579. 'entity_type' => $entity_type,
  580. 'bundle' => $bundle,
  581. 'context' => $context,
  582. 'mode' => $mode,
  583. );
  584. $groups[$group_name] = (object) $definition;
  585. }
  586. }
  587. return $groups;
  588. }
  589. /**
  590. * Loads a group definition.
  591. *
  592. * @param $group_name
  593. * The name of the group.
  594. * @param $entity_type
  595. * The name of the entity.
  596. * @param $bundle
  597. * The name of the bundle.
  598. * @param $context
  599. * The context of the view mode (form or view)
  600. * @param $mode
  601. * The view mode to load.
  602. */
  603. function field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode) {
  604. $groups = field_group_info_groups($entity_type, $bundle, $context, $mode);
  605. if (isset($groups[$group_name])) {
  606. return $groups[$group_name];
  607. }
  608. }
  609. /**
  610. * Checks if a field_group exists in required context.
  611. *
  612. * @param String $group_name
  613. * The name of the group.
  614. * @param String $entity_type
  615. * The name of the entity.
  616. * @param String $bundle
  617. * The bundle for the entity.
  618. * @param $context
  619. * The context of the view mode (form or view)
  620. * @param String $mode
  621. * The view mode context the group will be rendered.
  622. */
  623. function field_group_exists($group_name, $entity_type, $bundle, $context, $mode) {
  624. return (bool) field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode);
  625. }
  626. /**
  627. * Remove empty groups on forms.
  628. *
  629. * @param String $parent_name
  630. * The name of the element.
  631. * @param array $element
  632. * The element to check the empty state.
  633. * @param array $groups
  634. * Array of group objects.
  635. */
  636. function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) {
  637. $exceptions = array('user__account', 'comment__author');
  638. $children = Element::children($element);
  639. $hasChildren = FALSE;
  640. if (count($children)) {
  641. foreach ($children as $childname) {
  642. if (in_array($childname, $groups)) {
  643. field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity);
  644. }
  645. $exception = $entity . '__' . $childname;
  646. $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || isset($element[$childname]['#markup']) || in_array($exception, $exceptions));
  647. }
  648. }
  649. if (!$hasChildren) {
  650. // Remove empty elements from the #fieldgroups.
  651. if (empty($element) && isset($form_groups[$name]) && !is_array($form_groups[$name])) {
  652. foreach ($form_groups as $group_name => $group) {
  653. if (isset($group->children)) {
  654. $group_children = array_flip($group->children);
  655. if (isset($group_children[$name])) {
  656. unset($form_groups[$group_name]->children[$group_children[$name]]);
  657. }
  658. }
  659. }
  660. }
  661. $element['#access'] = FALSE;
  662. }
  663. }
  664. /**
  665. * Remove empty groups on entity display.
  666. *
  667. * @param array $element
  668. * The element to check the empty state.
  669. * @param array $groups
  670. * Array of group objects.
  671. */
  672. function field_group_remove_empty_display_groups(& $element, $groups) {
  673. $empty_child = TRUE;
  674. $empty_group = TRUE;
  675. // Loop through the visible children for current element.
  676. foreach (Element::getVisibleChildren($element) as $name) {
  677. // Descend if the child is a group.
  678. if (in_array($name, $groups)) {
  679. $empty_child = field_group_remove_empty_display_groups($element[$name], $groups);
  680. if (!$empty_child) {
  681. $empty_group = FALSE;
  682. }
  683. }
  684. // Child is a field or a renderable array and the element is not empty.
  685. elseif (!empty($element[$name])) {
  686. $clone_element = $element[$name];
  687. // Weight parameter can make empty element seen as not empty.
  688. unset($clone_element['#weight']);
  689. if (!Element::isEmpty($clone_element)) {
  690. $empty_group = FALSE;
  691. }
  692. }
  693. }
  694. // Reset an empty group.
  695. if ($empty_group) {
  696. $element = [];
  697. }
  698. return $empty_group;
  699. }