field_group.module 24 KB

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