file ProcessManager.php

Namespace

Drupal\bootstrap\Plugin
  1. <?php
  2. namespace Drupal\bootstrap\Plugin;
  3. use Drupal\bootstrap\Bootstrap;
  4. use Drupal\bootstrap\Theme;
  5. use Drupal\bootstrap\Utility\Element;
  6. use Drupal\Component\Utility\NestedArray;
  7. use Drupal\Core\Form\FormStateInterface;
  8. /**
  9. * Manages discovery and instantiation of Bootstrap form process callbacks.
  10. *
  11. * @ingroup plugins_process
  12. */
  13. class ProcessManager extends PluginManager {
  14. /**
  15. * A list of element types that should be rendered as inline.
  16. *
  17. * @var array
  18. *
  19. * @deprecated in bootstrap:8.x-3.21 and is removed from bootstrap:8.x-4.0.
  20. * This method will be removed when process managers can be sub-classed.
  21. *
  22. * @see https://www.drupal.org/project/bootstrap/issues/2868538
  23. *
  24. * @internal
  25. */
  26. protected static $inlineElementTypes;
  27. /**
  28. * Constructs a new \Drupal\bootstrap\Plugin\ProcessManager object.
  29. *
  30. * @param \Drupal\bootstrap\Theme $theme
  31. * The theme to use for discovery.
  32. */
  33. public function __construct(Theme $theme) {
  34. parent::__construct($theme, 'Plugin/Process', 'Drupal\bootstrap\Plugin\Process\ProcessInterface', 'Drupal\bootstrap\Annotation\BootstrapProcess');
  35. $this->setCacheBackend(\Drupal::cache('discovery'), 'theme:' . $theme->getName() . ':process', $this->getCacheTags());
  36. }
  37. /**
  38. * Global #process callback for form elements.
  39. *
  40. * @param array $element
  41. * The element render array.
  42. * @param \Drupal\Core\Form\FormStateInterface $form_state
  43. * The current state of the form.
  44. * @param array $complete_form
  45. * The complete form structure.
  46. *
  47. * @return array
  48. * The altered element array.
  49. *
  50. * @see \Drupal\bootstrap\Plugin\Alter\ElementInfo::alter
  51. */
  52. public static function process(array $element, FormStateInterface $form_state, array &$complete_form) {
  53. if (!empty($element['#bootstrap_ignore_process'])) {
  54. return $element;
  55. }
  56. static $theme;
  57. if (!isset($theme)) {
  58. $theme = Bootstrap::getTheme();
  59. }
  60. $e = Element::create($element, $form_state);
  61. // Process AJAX.
  62. if (($e->getProperty('ajax') && !$e->isButton()) || $e->getProperty('autocomplete_route_name')) {
  63. static::processAjax($e, $form_state, $complete_form);
  64. }
  65. // Add "form-inline" class to the container.
  66. if ($e->hasClass('container-inline')) {
  67. $e->replaceClass('container-inline', 'form-inline');
  68. }
  69. // Add "form-inline" class to certain element types.
  70. if ($e->isType(static::getInlineElementTypes())) {
  71. $e->addClass('form-inline', 'wrapper_attributes');
  72. }
  73. // Process input groups.
  74. if ($e->getProperty('input') && ($e->getProperty('input_group') || $e->getProperty('input_group_button'))) {
  75. static::processInputGroups($e, $form_state, $complete_form);
  76. }
  77. return $element;
  78. }
  79. /**
  80. * Retrieves the element types that should be rendered as inline.
  81. *
  82. * @return array
  83. * The inline element types.
  84. *
  85. * @deprecated in bootstrap:8.x-3.21 and is removed from bootstrap:8.x-4.0.
  86. * This method will be removed when process managers can be sub-classed.
  87. *
  88. * @see https://www.drupal.org/project/bootstrap/issues/2868538
  89. *
  90. * @internal
  91. */
  92. protected static function getInlineElementTypes() {
  93. if (!static::$inlineElementTypes) {
  94. $types = ['color', 'date', 'number', 'range', 'tel', 'weight'];
  95. \Drupal::theme()->alter('bootstrap_inline_element_types', $types);
  96. static::$inlineElementTypes = $types;
  97. }
  98. return static::$inlineElementTypes;
  99. }
  100. /**
  101. * Processes elements with AJAX properties.
  102. *
  103. * @param \Drupal\bootstrap\Utility\Element $element
  104. * The element object.
  105. * @param \Drupal\Core\Form\FormStateInterface $form_state
  106. * The current state of the form.
  107. * @param array $complete_form
  108. * The complete form structure.
  109. */
  110. public static function processAjax(Element $element, FormStateInterface $form_state, array &$complete_form) {
  111. $ajax = $element->getProperty('ajax');
  112. // Show throbber AJAX requests in an input button group.
  113. $ignore_types = ['checkbox', 'checkboxes', 'hidden', 'radio', 'radios'];
  114. if ((!isset($ajax['progress']['type']) || $ajax['progress']['type'] === 'throbber') && !$element->isType($ignore_types)) {
  115. // Use an icon for autocomplete "throbber".
  116. $icon = Bootstrap::glyphicon('refresh');
  117. $element->appendProperty('field_suffix', Element::create($icon)->addClass(['ajax-progress', 'ajax-progress-throbber']));
  118. $element->setProperty('input_group', TRUE);
  119. }
  120. }
  121. /**
  122. * Processes elements that have input groups.
  123. *
  124. * @param \Drupal\bootstrap\Utility\Element $element
  125. * The element object.
  126. * @param \Drupal\Core\Form\FormStateInterface $form_state
  127. * The current state of the form.
  128. * @param array $complete_form
  129. * The complete form structure.
  130. */
  131. protected static function processInputGroups(Element $element, FormStateInterface $form_state, array &$complete_form) {
  132. // Automatically inject the nearest button found after this element if
  133. // #input_group_button exists.
  134. if ($element->getProperty('input_group_button')) {
  135. // Obtain the parent array to limit search.
  136. $array_parents = $element->getProperty('array_parents', []);
  137. // Remove the current element from the array.
  138. array_pop($array_parents);
  139. // Retrieve the parent element.
  140. $parent = Element::create(NestedArray::getValue($complete_form, $array_parents), $form_state);
  141. // Find the closest button.
  142. if ($button = &$parent->findButton()) {
  143. // Since this button is technically being "moved", it needs to be
  144. // rendered now, so it doesn't get printed twice (in the original spot).
  145. $element->appendProperty('field_suffix', $button->setIcon()->render());
  146. }
  147. }
  148. $input_group_attributes = ['class' => ['input-group-' . ($element->getProperty('input_group_button') ? 'btn' : 'addon')]];
  149. if ($prefix = $element->getProperty('field_prefix')) {
  150. $element->setProperty('field_prefix', [
  151. '#type' => 'html_tag',
  152. '#tag' => 'span',
  153. '#attributes' => $input_group_attributes,
  154. '#value' => Element::create($prefix)->renderPlain(),
  155. '#weight' => -1,
  156. ]);
  157. }
  158. if ($suffix = $element->getProperty('field_suffix')) {
  159. $element->setProperty('field_suffix', [
  160. '#type' => 'html_tag',
  161. '#tag' => 'span',
  162. '#attributes' => $input_group_attributes,
  163. '#value' => Element::create($suffix)->renderPlain(),
  164. '#weight' => 1,
  165. ]);
  166. }
  167. }
  168. /**
  169. * Traverses an element to find the closest button.
  170. *
  171. * @param \Drupal\bootstrap\Utility\Element $element
  172. * The element to iterate over.
  173. *
  174. * @return \Drupal\bootstrap\Utility\Element|false
  175. * The first button element or FALSE if no button could be found.
  176. *
  177. * @deprecated Will be removed in a future release.
  178. * Use \Drupal\bootstrap\Utility\Element::findButton() directly.
  179. */
  180. protected static function &findButton(Element $element) {
  181. Bootstrap::deprecated();
  182. return $element->findButton();
  183. }
  184. }

Classes

Name Description
ProcessManager Manages discovery and instantiation of Bootstrap form process callbacks.