FormBuilder.php 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408
  1. <?php
  2. namespace Drupal\Core\Form;
  3. use Drupal\Component\Utility\Crypt;
  4. use Drupal\Component\Utility\Html;
  5. use Drupal\Component\Utility\NestedArray;
  6. use Drupal\Component\Utility\UrlHelper;
  7. use Drupal\Core\Access\AccessResultInterface;
  8. use Drupal\Core\Access\CsrfTokenGenerator;
  9. use Drupal\Core\DependencyInjection\ClassResolverInterface;
  10. use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
  11. use Drupal\Core\Extension\ModuleHandlerInterface;
  12. use Drupal\Core\Form\Exception\BrokenPostRequestException;
  13. use Drupal\Core\Render\Element;
  14. use Drupal\Core\Render\ElementInfoManagerInterface;
  15. use Drupal\Core\Theme\ThemeManagerInterface;
  16. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  17. use Symfony\Component\HttpFoundation\FileBag;
  18. use Symfony\Component\HttpFoundation\RequestStack;
  19. use Symfony\Component\HttpFoundation\Response;
  20. /**
  21. * Provides form building and processing.
  22. *
  23. * @ingroup form_api
  24. */
  25. class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface {
  26. /**
  27. * The module handler.
  28. *
  29. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  30. */
  31. protected $moduleHandler;
  32. /**
  33. * The event dispatcher.
  34. *
  35. * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
  36. */
  37. protected $eventDispatcher;
  38. /**
  39. * The request stack.
  40. *
  41. * @var \Symfony\Component\HttpFoundation\RequestStack
  42. */
  43. protected $requestStack;
  44. /**
  45. * The element info manager.
  46. *
  47. * @var \Drupal\Core\Render\ElementInfoManagerInterface
  48. */
  49. protected $elementInfo;
  50. /**
  51. * The CSRF token generator to validate the form token.
  52. *
  53. * @var \Drupal\Core\Access\CsrfTokenGenerator
  54. */
  55. protected $csrfToken;
  56. /**
  57. * The class resolver.
  58. *
  59. * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
  60. */
  61. protected $classResolver;
  62. /**
  63. * The current user.
  64. *
  65. * @var \Drupal\Core\Session\AccountInterface
  66. */
  67. protected $currentUser;
  68. /**
  69. * The theme manager.
  70. *
  71. * @var \Drupal\Core\Theme\ThemeManagerInterface
  72. */
  73. protected $themeManager;
  74. /**
  75. * The form validator.
  76. *
  77. * @var \Drupal\Core\Form\FormValidatorInterface
  78. */
  79. protected $formValidator;
  80. /**
  81. * The form submitter.
  82. *
  83. * @var \Drupal\Core\Form\FormSubmitterInterface
  84. */
  85. protected $formSubmitter;
  86. /**
  87. * The form cache.
  88. *
  89. * @var \Drupal\Core\Form\FormCacheInterface
  90. */
  91. protected $formCache;
  92. /**
  93. * Defines element value callables which are safe to run even when the form
  94. * state has an invalid CSRF token.
  95. *
  96. * Excluded from this list on purpose:
  97. * - Drupal\file\Element\ManagedFile::valueCallback
  98. * - Drupal\Core\Datetime\Element\Datelist::valueCallback
  99. * - Drupal\Core\Datetime\Element\Datetime::valueCallback
  100. * - Drupal\Core\Render\Element\ImageButton::valueCallback
  101. * - Drupal\file\Plugin\Field\FieldWidget\FileWidget::value
  102. * - color_palette_color_value
  103. *
  104. * @var array
  105. */
  106. protected $safeCoreValueCallables = [
  107. 'Drupal\Core\Render\Element\Checkbox::valueCallback',
  108. 'Drupal\Core\Render\Element\Checkboxes::valueCallback',
  109. 'Drupal\Core\Render\Element\Email::valueCallback',
  110. 'Drupal\Core\Render\Element\FormElement::valueCallback',
  111. 'Drupal\Core\Render\Element\MachineName::valueCallback',
  112. 'Drupal\Core\Render\Element\Number::valueCallback',
  113. 'Drupal\Core\Render\Element\PathElement::valueCallback',
  114. 'Drupal\Core\Render\Element\Password::valueCallback',
  115. 'Drupal\Core\Render\Element\PasswordConfirm::valueCallback',
  116. 'Drupal\Core\Render\Element\Radio::valueCallback',
  117. 'Drupal\Core\Render\Element\Radios::valueCallback',
  118. 'Drupal\Core\Render\Element\Range::valueCallback',
  119. 'Drupal\Core\Render\Element\Search::valueCallback',
  120. 'Drupal\Core\Render\Element\Select::valueCallback',
  121. 'Drupal\Core\Render\Element\Tableselect::valueCallback',
  122. 'Drupal\Core\Render\Element\Table::valueCallback',
  123. 'Drupal\Core\Render\Element\Tel::valueCallback',
  124. 'Drupal\Core\Render\Element\Textarea::valueCallback',
  125. 'Drupal\Core\Render\Element\Textfield::valueCallback',
  126. 'Drupal\Core\Render\Element\Token::valueCallback',
  127. 'Drupal\Core\Render\Element\Url::valueCallback',
  128. 'Drupal\Core\Render\Element\Weight::valueCallback',
  129. ];
  130. /**
  131. * Constructs a new FormBuilder.
  132. *
  133. * @param \Drupal\Core\Form\FormValidatorInterface $form_validator
  134. * The form validator.
  135. * @param \Drupal\Core\Form\FormSubmitterInterface $form_submitter
  136. * The form submission processor.
  137. * @param \Drupal\Core\Form\FormCacheInterface $form_cache
  138. * The form cache.
  139. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  140. * The module handler.
  141. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
  142. * The event dispatcher.
  143. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
  144. * The request stack.
  145. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
  146. * The class resolver.
  147. * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
  148. * The element info manager.
  149. * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
  150. * The theme manager.
  151. * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
  152. * The CSRF token generator.
  153. */
  154. public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL) {
  155. $this->formValidator = $form_validator;
  156. $this->formSubmitter = $form_submitter;
  157. $this->formCache = $form_cache;
  158. $this->moduleHandler = $module_handler;
  159. $this->eventDispatcher = $event_dispatcher;
  160. $this->requestStack = $request_stack;
  161. $this->classResolver = $class_resolver;
  162. $this->elementInfo = $element_info;
  163. $this->csrfToken = $csrf_token;
  164. $this->themeManager = $theme_manager;
  165. }
  166. /**
  167. * {@inheritdoc}
  168. */
  169. public function getFormId($form_arg, FormStateInterface &$form_state) {
  170. // If the $form_arg is the name of a class, instantiate it. Don't allow
  171. // arbitrary strings to be passed to the class resolver.
  172. if (is_string($form_arg) && class_exists($form_arg)) {
  173. $form_arg = $this->classResolver->getInstanceFromDefinition($form_arg);
  174. }
  175. if (!is_object($form_arg) || !($form_arg instanceof FormInterface)) {
  176. throw new \InvalidArgumentException("The form argument $form_arg is not a valid form.");
  177. }
  178. // Add the $form_arg as the callback object and determine the form ID.
  179. $form_state->setFormObject($form_arg);
  180. if ($form_arg instanceof BaseFormIdInterface) {
  181. $form_state->addBuildInfo('base_form_id', $form_arg->getBaseFormId());
  182. }
  183. return $form_arg->getFormId();
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. public function getForm($form_arg) {
  189. $form_state = new FormState();
  190. $args = func_get_args();
  191. // Remove $form_arg from the arguments.
  192. unset($args[0]);
  193. $form_state->addBuildInfo('args', array_values($args));
  194. return $this->buildForm($form_arg, $form_state);
  195. }
  196. /**
  197. * {@inheritdoc}
  198. */
  199. public function buildForm($form_id, FormStateInterface &$form_state) {
  200. // Ensure the form ID is prepared.
  201. $form_id = $this->getFormId($form_id, $form_state);
  202. $request = $this->requestStack->getCurrentRequest();
  203. // Inform $form_state about the request method that's building it, so that
  204. // it can prevent persisting state changes during HTTP methods for which
  205. // that is disallowed by HTTP: GET and HEAD.
  206. $form_state->setRequestMethod($request->getMethod());
  207. // Initialize the form's user input. The user input should include only the
  208. // input meant to be treated as part of what is submitted to the form, so
  209. // we base it on the form's method rather than the request's method. For
  210. // example, when someone does a GET request for
  211. // /node/add/article?destination=foo, which is a form that expects its
  212. // submission method to be POST, the user input during the GET request
  213. // should be initialized to empty rather than to ['destination' => 'foo'].
  214. $input = $form_state->getUserInput();
  215. if (!isset($input)) {
  216. $input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();
  217. $form_state->setUserInput($input);
  218. }
  219. if (isset($_SESSION['batch_form_state'])) {
  220. // We've been redirected here after a batch processing. The form has
  221. // already been processed, but needs to be rebuilt. See _batch_finished().
  222. $form_state = $_SESSION['batch_form_state'];
  223. unset($_SESSION['batch_form_state']);
  224. return $this->rebuildForm($form_id, $form_state);
  225. }
  226. // If the incoming input contains a form_build_id, we'll check the cache for
  227. // a copy of the form in question. If it's there, we don't have to rebuild
  228. // the form to proceed. In addition, if there is stored form_state data from
  229. // a previous step, we'll retrieve it so it can be passed on to the form
  230. // processing code.
  231. $check_cache = isset($input['form_id']) && $input['form_id'] == $form_id && !empty($input['form_build_id']);
  232. if ($check_cache) {
  233. $form = $this->getCache($input['form_build_id'], $form_state);
  234. }
  235. // If the previous bit of code didn't result in a populated $form object, we
  236. // are hitting the form for the first time and we need to build it from
  237. // scratch.
  238. if (!isset($form)) {
  239. // If we attempted to serve the form from cache, uncacheable $form_state
  240. // keys need to be removed after retrieving and preparing the form, except
  241. // any that were already set prior to retrieving the form.
  242. if ($check_cache) {
  243. $form_state_before_retrieval = clone $form_state;
  244. }
  245. $form = $this->retrieveForm($form_id, $form_state);
  246. $this->prepareForm($form_id, $form, $form_state);
  247. // self::setCache() removes uncacheable $form_state keys (see properties
  248. // in \Drupal\Core\Form\FormState) in order for multi-step forms to work
  249. // properly. This means that form processing logic for single-step forms
  250. // using $form_state->isCached() may depend on data stored in those keys
  251. // during self::retrieveForm()/self::prepareForm(), but form processing
  252. // should not depend on whether the form is cached or not, so $form_state
  253. // is adjusted to match what it would be after a
  254. // self::setCache()/self::getCache() sequence. These exceptions are
  255. // allowed to survive here:
  256. // - always_process: Does not make sense in conjunction with form caching
  257. // in the first place, since passing form_build_id as a GET parameter is
  258. // not desired.
  259. // - temporary: Any assigned data is expected to survives within the same
  260. // page request.
  261. if ($check_cache) {
  262. $cache_form_state = $form_state->getCacheableArray();
  263. $cache_form_state['always_process'] = $form_state->getAlwaysProcess();
  264. $cache_form_state['temporary'] = $form_state->getTemporary();
  265. $form_state = $form_state_before_retrieval;
  266. $form_state->setFormState($cache_form_state);
  267. }
  268. }
  269. // If this form is an AJAX request, disable all form redirects.
  270. $request = $this->requestStack->getCurrentRequest();
  271. if ($ajax_form_request = $request->query->has(static::AJAX_FORM_REQUEST)) {
  272. $form_state->disableRedirect();
  273. }
  274. // Now that we have a constructed form, process it. This is where:
  275. // - Element #process functions get called to further refine $form.
  276. // - User input, if any, gets incorporated in the #value property of the
  277. // corresponding elements and into $form_state->getValues().
  278. // - Validation and submission handlers are called.
  279. // - If this submission is part of a multistep workflow, the form is rebuilt
  280. // to contain the information of the next step.
  281. // - If necessary, the form and form state are cached or re-cached, so that
  282. // appropriate information persists to the next page request.
  283. // All of the handlers in the pipeline receive $form_state by reference and
  284. // can use it to know or update information about the state of the form.
  285. $response = $this->processForm($form_id, $form, $form_state);
  286. // In case the post request exceeds the configured allowed size
  287. // (post_max_size), the post request is potentially broken. Add some
  288. // protection against that and at the same time have a nice error message.
  289. if ($ajax_form_request && !$request->request->has('form_id')) {
  290. throw new BrokenPostRequestException($this->getFileUploadMaxSize());
  291. }
  292. // After processing the form, if this is an AJAX form request, interrupt
  293. // form rendering and return by throwing an exception that contains the
  294. // processed form and form state. This exception will be caught by
  295. // \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber::onException() and
  296. // then passed through
  297. // \Drupal\Core\Form\FormAjaxResponseBuilderInterface::buildResponse() to
  298. // build a proper AJAX response.
  299. // Only do this when the form ID matches, since there is no guarantee from
  300. // $ajax_form_request that it's an AJAX request for this particular form.
  301. if ($ajax_form_request && $form_state->isProcessingInput() && $request->request->get('form_id') == $form_id) {
  302. throw new FormAjaxException($form, $form_state);
  303. }
  304. // If the form returns a response, skip subsequent page construction by
  305. // throwing an exception.
  306. // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
  307. //
  308. // @todo Exceptions should not be used for code flow control. However, the
  309. // Form API does not integrate with the HTTP Kernel based architecture of
  310. // Drupal 8. In order to resolve this issue properly it is necessary to
  311. // completely separate form submission from rendering.
  312. // @see https://www.drupal.org/node/2367555
  313. if ($response instanceof Response) {
  314. throw new EnforcedResponseException($response);
  315. }
  316. // If this was a successful submission of a single-step form or the last
  317. // step of a multi-step form, then self::processForm() issued a redirect to
  318. // another page, or back to this page, but as a new request. Therefore, if
  319. // we're here, it means that this is either a form being viewed initially
  320. // before any user input, or there was a validation error requiring the form
  321. // to be re-displayed, or we're in a multi-step workflow and need to display
  322. // the form's next step. In any case, we have what we need in $form, and can
  323. // return it for rendering.
  324. return $form;
  325. }
  326. /**
  327. * {@inheritdoc}
  328. */
  329. public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) {
  330. $form = $this->retrieveForm($form_id, $form_state);
  331. // Only GET and POST are valid form methods. If the form receives its input
  332. // via POST, then $form_state must be persisted when it is rebuilt between
  333. // submissions. If the form receives its input via GET, then persisting
  334. // state is forbidden by $form_state->setCached(), and the form must use
  335. // the URL itself to transfer its state across steps. Although $form_state
  336. // throws an exception based on the request method rather than the form's
  337. // method, we base the decision to cache on the form method, because:
  338. // - It's the form method that defines what the form needs to do to manage
  339. // its state.
  340. // - rebuildForm() should only be called after successful input processing,
  341. // which means the request method matches the form method, and if not,
  342. // there's some other error, so it's ok if an exception is thrown.
  343. if ($form_state->isMethodType('POST')) {
  344. $form_state->setCached();
  345. }
  346. // If only parts of the form will be returned to the browser (e.g., Ajax or
  347. // RIA clients), or if the form already had a new build ID regenerated when
  348. // it was retrieved from the form cache, reuse the existing #build_id.
  349. // Otherwise, a new #build_id is generated, to not clobber the previous
  350. // build's data in the form cache; also allowing the user to go back to an
  351. // earlier build, make changes, and re-submit.
  352. // @see self::prepareForm()
  353. $rebuild_info = $form_state->getRebuildInfo();
  354. $enforce_old_build_id = isset($old_form['#build_id']) && !empty($rebuild_info['copy']['#build_id']);
  355. $old_form_is_mutable_copy = isset($old_form['#build_id_old']);
  356. if ($enforce_old_build_id || $old_form_is_mutable_copy) {
  357. $form['#build_id'] = $old_form['#build_id'];
  358. if ($old_form_is_mutable_copy) {
  359. $form['#build_id_old'] = $old_form['#build_id_old'];
  360. }
  361. }
  362. else {
  363. if (isset($old_form['#build_id'])) {
  364. $form['#build_id_old'] = $old_form['#build_id'];
  365. }
  366. $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
  367. }
  368. // #action defaults to $request->getRequestUri(), but in case of Ajax and
  369. // other partial rebuilds, the form is submitted to an alternate URL, and
  370. // the original #action needs to be retained.
  371. if (isset($old_form['#action']) && !empty($rebuild_info['copy']['#action'])) {
  372. $form['#action'] = $old_form['#action'];
  373. }
  374. $this->prepareForm($form_id, $form, $form_state);
  375. // Caching is normally done in self::processForm(), but what needs to be
  376. // cached is the $form structure before it passes through
  377. // self::doBuildForm(), so we need to do it here.
  378. // @todo For Drupal 8, find a way to avoid this code duplication.
  379. if ($form_state->isCached()) {
  380. $this->setCache($form['#build_id'], $form, $form_state);
  381. }
  382. // Clear out all group associations as these might be different when
  383. // re-rendering the form.
  384. $form_state->setGroups([]);
  385. // Return a fully built form that is ready for rendering.
  386. return $this->doBuildForm($form_id, $form, $form_state);
  387. }
  388. /**
  389. * {@inheritdoc}
  390. */
  391. public function getCache($form_build_id, FormStateInterface $form_state) {
  392. return $this->formCache->getCache($form_build_id, $form_state);
  393. }
  394. /**
  395. * {@inheritdoc}
  396. */
  397. public function setCache($form_build_id, $form, FormStateInterface $form_state) {
  398. $this->formCache->setCache($form_build_id, $form, $form_state);
  399. }
  400. /**
  401. * {@inheritdoc}
  402. */
  403. public function deleteCache($form_build_id) {
  404. $this->formCache->deleteCache($form_build_id);
  405. }
  406. /**
  407. * {@inheritdoc}
  408. */
  409. public function submitForm($form_arg, FormStateInterface &$form_state) {
  410. $build_info = $form_state->getBuildInfo();
  411. if (empty($build_info['args'])) {
  412. $args = func_get_args();
  413. // Remove $form and $form_state from the arguments.
  414. unset($args[0], $args[1]);
  415. $form_state->addBuildInfo('args', array_values($args));
  416. }
  417. // Populate FormState::$input with the submitted values before retrieving
  418. // the form, to be consistent with what self::buildForm() does for
  419. // non-programmatic submissions (form builder functions may expect it to be
  420. // there).
  421. $form_state->setUserInput($form_state->getValues());
  422. $form_state->setProgrammed();
  423. $form_id = $this->getFormId($form_arg, $form_state);
  424. $form = $this->retrieveForm($form_id, $form_state);
  425. // Programmed forms are always submitted.
  426. $form_state->setSubmitted();
  427. // Reset form validation.
  428. $form_state->setValidationEnforced();
  429. $form_state->clearErrors();
  430. $this->prepareForm($form_id, $form, $form_state);
  431. $this->processForm($form_id, $form, $form_state);
  432. }
  433. /**
  434. * {@inheritdoc}
  435. */
  436. public function retrieveForm($form_id, FormStateInterface &$form_state) {
  437. // Record the $form_id.
  438. $form_state->addBuildInfo('form_id', $form_id);
  439. // We save two copies of the incoming arguments: one for modules to use
  440. // when mapping form ids to constructor functions, and another to pass to
  441. // the constructor function itself.
  442. $build_info = $form_state->getBuildInfo();
  443. $args = $build_info['args'];
  444. $callback = [$form_state->getFormObject(), 'buildForm'];
  445. $form = [];
  446. // Assign a default CSS class name based on $form_id.
  447. // This happens here and not in self::prepareForm() in order to allow the
  448. // form constructor function to override or remove the default class.
  449. $form['#attributes']['class'][] = Html::getClass($form_id);
  450. // Same for the base form ID, if any.
  451. if (isset($build_info['base_form_id'])) {
  452. $form['#attributes']['class'][] = Html::getClass($build_info['base_form_id']);
  453. }
  454. // We need to pass $form_state by reference in order for forms to modify it,
  455. // since call_user_func_array() requires that referenced variables are
  456. // passed explicitly.
  457. $args = array_merge([$form, &$form_state], $args);
  458. $form = call_user_func_array($callback, $args);
  459. // If the form returns a response, skip subsequent page construction by
  460. // throwing an exception.
  461. // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
  462. //
  463. // @todo Exceptions should not be used for code flow control. However, the
  464. // Form API currently allows any form builder functions to return a
  465. // response.
  466. // @see https://www.drupal.org/node/2363189
  467. if ($form instanceof Response) {
  468. throw new EnforcedResponseException($form);
  469. }
  470. $form['#form_id'] = $form_id;
  471. return $form;
  472. }
  473. /**
  474. * {@inheritdoc}
  475. */
  476. public function processForm($form_id, &$form, FormStateInterface &$form_state) {
  477. $form_state->setValues([]);
  478. // With GET, these forms are always submitted if requested.
  479. if ($form_state->isMethodType('get') && $form_state->getAlwaysProcess()) {
  480. $input = $form_state->getUserInput();
  481. if (!isset($input['form_build_id'])) {
  482. $input['form_build_id'] = $form['#build_id'];
  483. }
  484. if (!isset($input['form_id'])) {
  485. $input['form_id'] = $form_id;
  486. }
  487. if (!isset($input['form_token']) && isset($form['#token'])) {
  488. $input['form_token'] = $this->csrfToken->get($form['#token']);
  489. }
  490. $form_state->setUserInput($input);
  491. }
  492. // self::doBuildForm() finishes building the form by calling element
  493. // #process functions and mapping user input, if any, to #value properties,
  494. // and also storing the values in $form_state->getValues(). We need to
  495. // retain the unprocessed $form in case it needs to be cached.
  496. $unprocessed_form = $form;
  497. $form = $this->doBuildForm($form_id, $form, $form_state);
  498. // Only process the input if we have a correct form submission.
  499. if ($form_state->isProcessingInput()) {
  500. // Form values for programmed form submissions typically do not include a
  501. // value for the submit button. But without a triggering element, a
  502. // potentially existing #limit_validation_errors property on the primary
  503. // submit button is not taken account. Therefore, check whether there is
  504. // exactly one submit button in the form, and if so, automatically use it
  505. // as triggering_element.
  506. $buttons = $form_state->getButtons();
  507. if ($form_state->isProgrammed() && !$form_state->getTriggeringElement() && count($buttons) == 1) {
  508. $form_state->setTriggeringElement(reset($buttons));
  509. }
  510. $this->formValidator->validateForm($form_id, $form, $form_state);
  511. // \Drupal\Component\Utility\Html::getUniqueId() maintains a cache of
  512. // element IDs it has seen, so it can prevent duplicates. We want to be
  513. // sure we reset that cache when a form is processed, so scenarios that
  514. // result in the form being built behind the scenes and again for the
  515. // browser don't increment all the element IDs needlessly.
  516. if (!FormState::hasAnyErrors()) {
  517. // In case of errors, do not break HTML IDs of other forms.
  518. Html::resetSeenIds();
  519. }
  520. // If there are no errors and the form is not rebuilding, submit the form.
  521. if (!$form_state->isRebuilding() && !FormState::hasAnyErrors()) {
  522. $submit_response = $this->formSubmitter->doSubmitForm($form, $form_state);
  523. // If this form was cached, delete it from the cache after submission.
  524. if ($form_state->isCached()) {
  525. $this->deleteCache($form['#build_id']);
  526. }
  527. // If the form submission directly returned a response, return it now.
  528. if ($submit_response) {
  529. return $submit_response;
  530. }
  531. }
  532. // Don't rebuild or cache form submissions invoked via self::submitForm().
  533. if ($form_state->isProgrammed()) {
  534. return;
  535. }
  536. // If $form_state->isRebuilding() has been set and input has been
  537. // processed without validation errors, we are in a multi-step workflow
  538. // that is not yet complete. A new $form needs to be constructed based on
  539. // the changes made to $form_state during this request. Normally, a submit
  540. // handler sets $form_state->isRebuilding() if a fully executed form
  541. // requires another step. However, for forms that have not been fully
  542. // executed (e.g., Ajax submissions triggered by non-buttons), there is no
  543. // submit handler to set $form_state->isRebuilding(). It would not make
  544. // sense to redisplay the identical form without an error for the user to
  545. // correct, so we also rebuild error-free non-executed forms, regardless
  546. // of $form_state->isRebuilding().
  547. // @todo Simplify this logic; considering Ajax and non-HTML front-ends,
  548. // along with element-level #submit properties, it makes no sense to
  549. // have divergent form execution based on whether the triggering element
  550. // has #executes_submit_callback set to TRUE.
  551. if (($form_state->isRebuilding() || !$form_state->isExecuted()) && !FormState::hasAnyErrors()) {
  552. // Form building functions (e.g., self::handleInputElement()) may use
  553. // $form_state->isRebuilding() to determine if they are running in the
  554. // context of a rebuild, so ensure it is set.
  555. $form_state->setRebuild();
  556. $form = $this->rebuildForm($form_id, $form_state, $form);
  557. }
  558. }
  559. // After processing the form, the form builder or a #process callback may
  560. // have called $form_state->setCached() to indicate that the form and form
  561. // state shall be cached. But the form may only be cached if
  562. // $form_state->disableCache() is not called. Only cache $form as it was
  563. // prior to self::doBuildForm(), because self::doBuildForm() must run for
  564. // each request to accommodate new user input. Rebuilt forms are not cached
  565. // here, because self::rebuildForm() already takes care of that.
  566. if (!$form_state->isRebuilding() && $form_state->isCached()) {
  567. $this->setCache($form['#build_id'], $unprocessed_form, $form_state);
  568. }
  569. }
  570. /**
  571. * #lazy_builder callback; renders a form action URL.
  572. *
  573. * @return array
  574. * A renderable array representing the form action.
  575. */
  576. public function renderPlaceholderFormAction() {
  577. return [
  578. '#type' => 'markup',
  579. '#markup' => $this->buildFormAction(),
  580. '#cache' => ['contexts' => ['url.path', 'url.query_args']],
  581. ];
  582. }
  583. /**
  584. * #lazy_builder callback; renders form CSRF token.
  585. *
  586. * @param string $placeholder
  587. * A string containing a placeholder, matching the value of the form's
  588. * #token.
  589. *
  590. * @return array
  591. * A renderable array containing the CSRF token.
  592. */
  593. public function renderFormTokenPlaceholder($placeholder) {
  594. return [
  595. '#markup' => $this->csrfToken->get($placeholder),
  596. '#cache' => [
  597. 'contexts' => [
  598. 'session',
  599. ],
  600. ],
  601. ];
  602. }
  603. /**
  604. * {@inheritdoc}
  605. */
  606. public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
  607. $user = $this->currentUser();
  608. $form['#type'] = 'form';
  609. // Only update the action if it is not already set.
  610. if (!isset($form['#action'])) {
  611. // Instead of setting an actual action URL, we set the placeholder, which
  612. // will be replaced at the very last moment. This ensures forms with
  613. // dynamically generated action URLs don't have poor cacheability.
  614. // Use the proper API to generate the placeholder, when we have one. See
  615. // https://www.drupal.org/node/2562341. The placeholder uses a fixed string
  616. // that is Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm');
  617. $placeholder = 'form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM';
  618. $form['#attached']['placeholders'][$placeholder] = [
  619. '#lazy_builder' => ['form_builder:renderPlaceholderFormAction', []],
  620. ];
  621. $form['#action'] = $placeholder;
  622. }
  623. // Fix the form method, if it is 'get' in $form_state, but not in $form.
  624. if ($form_state->isMethodType('get') && !isset($form['#method'])) {
  625. $form['#method'] = 'get';
  626. }
  627. // GET forms should not use a CSRF token.
  628. if (isset($form['#method']) && $form['#method'] === 'get') {
  629. // Merges in a default, this means if you've explicitly set #token to the
  630. // the $form_id on a GET form, which we don't recommend, it will work.
  631. $form += [
  632. '#token' => FALSE,
  633. ];
  634. }
  635. // Generate a new #build_id for this form, if none has been set already.
  636. // The form_build_id is used as key to cache a particular build of the form.
  637. // For multi-step forms, this allows the user to go back to an earlier
  638. // build, make changes, and re-submit.
  639. // @see self::buildForm()
  640. // @see self::rebuildForm()
  641. if (!isset($form['#build_id'])) {
  642. $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
  643. }
  644. $form['form_build_id'] = [
  645. '#type' => 'hidden',
  646. '#value' => $form['#build_id'],
  647. '#id' => $form['#build_id'],
  648. '#name' => 'form_build_id',
  649. // Form processing and validation requires this value, so ensure the
  650. // submitted form value appears literally, regardless of custom #tree
  651. // and #parents being set elsewhere.
  652. '#parents' => ['form_build_id'],
  653. // Prevent user agents from prefilling the build id with earlier values.
  654. // When the ajax command "update_build_id" is executed, the user agent
  655. // will assume that a user interaction changed the field. Upon a soft
  656. // reload of the page, the previous build id will be restored in the
  657. // input, causing subsequent ajax callbacks to access the wrong cached
  658. // form build. Setting the autocomplete attribute to "off" will tell the
  659. // user agent to never reuse the value.
  660. // @see https://www.w3.org/TR/2011/WD-html5-20110525/common-input-element-attributes.html#the-autocomplete-attribute
  661. '#attributes' => [
  662. 'autocomplete' => 'off',
  663. ],
  664. ];
  665. // Add a token, based on either #token or form_id, to any form displayed to
  666. // authenticated users. This ensures that any submitted form was actually
  667. // requested previously by the user and protects against cross site request
  668. // forgeries.
  669. // This does not apply to programmatically submitted forms. Furthermore,
  670. // since tokens are session-bound and forms displayed to anonymous users are
  671. // very likely cached, we cannot assign a token for them.
  672. // During installation, there is no $user yet.
  673. // Form constructors may explicitly set #token to FALSE when cross site
  674. // request forgery is irrelevant to the form, such as search forms.
  675. if ($form_state->isProgrammed() || (isset($form['#token']) && $form['#token'] === FALSE)) {
  676. unset($form['#token']);
  677. }
  678. else {
  679. $form['#cache']['contexts'][] = 'user.roles:authenticated';
  680. if ($user && $user->isAuthenticated()) {
  681. // Generate a public token based on the form id.
  682. // Generates a placeholder based on the form ID.
  683. $placeholder = 'form_token_placeholder_' . Crypt::hashBase64($form_id);
  684. $form['#token'] = $placeholder;
  685. $form['form_token'] = [
  686. '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
  687. '#type' => 'token',
  688. '#default_value' => $placeholder,
  689. // Form processing and validation requires this value, so ensure the
  690. // submitted form value appears literally, regardless of custom #tree
  691. // and #parents being set elsewhere.
  692. '#parents' => ['form_token'],
  693. // Instead of setting an actual CSRF token, we've set the placeholder
  694. // in form_token's #default_value and #placeholder. These will be
  695. // replaced at the very last moment. This ensures forms with a CSRF
  696. // token don't have poor cacheability.
  697. '#attached' => [
  698. 'placeholders' => [
  699. $placeholder => [
  700. '#lazy_builder' => ['form_builder:renderFormTokenPlaceholder', [$placeholder]],
  701. ],
  702. ],
  703. ],
  704. '#cache' => [
  705. 'max-age' => 0,
  706. ],
  707. ];
  708. }
  709. }
  710. if (isset($form_id)) {
  711. $form['form_id'] = [
  712. '#type' => 'hidden',
  713. '#value' => $form_id,
  714. '#id' => Html::getUniqueId("edit-$form_id"),
  715. // Form processing and validation requires this value, so ensure the
  716. // submitted form value appears literally, regardless of custom #tree
  717. // and #parents being set elsewhere.
  718. '#parents' => ['form_id'],
  719. ];
  720. }
  721. if (!isset($form['#id'])) {
  722. $form['#id'] = Html::getUniqueId($form_id);
  723. // Provide a selector usable by JavaScript. As the ID is unique, its not
  724. // possible to rely on it in JavaScript.
  725. $form['#attributes']['data-drupal-selector'] = Html::getId($form_id);
  726. }
  727. $form += $this->elementInfo->getInfo('form');
  728. $form += ['#tree' => FALSE, '#parents' => []];
  729. $form['#validate'][] = '::validateForm';
  730. $form['#submit'][] = '::submitForm';
  731. $build_info = $form_state->getBuildInfo();
  732. // If no #theme has been set, automatically apply theme suggestions.
  733. // The form theme hook itself, which is rendered by form.html.twig,
  734. // is in #theme_wrappers. Therefore, the #theme function only has to care
  735. // for rendering the inner form elements, not the form itself.
  736. if (!isset($form['#theme'])) {
  737. $form['#theme'] = [$form_id];
  738. if (isset($build_info['base_form_id'])) {
  739. $form['#theme'][] = $build_info['base_form_id'];
  740. }
  741. }
  742. // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
  743. // hook_form_FORM_ID_alter() implementations.
  744. $hooks = ['form'];
  745. if (isset($build_info['base_form_id'])) {
  746. $hooks[] = 'form_' . $build_info['base_form_id'];
  747. }
  748. $hooks[] = 'form_' . $form_id;
  749. $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
  750. $this->themeManager->alter($hooks, $form, $form_state, $form_id);
  751. }
  752. /**
  753. * Builds the $form['#action'].
  754. *
  755. * @return string
  756. * The URL to be used as the $form['#action'].
  757. */
  758. protected function buildFormAction() {
  759. // @todo Use <current> instead of the master request in
  760. // https://www.drupal.org/node/2505339.
  761. $request = $this->requestStack->getMasterRequest();
  762. $request_uri = $request->getRequestUri();
  763. // Prevent cross site requests via the Form API by using an absolute URL
  764. // when the request uri starts with multiple slashes..
  765. if (strpos($request_uri, '//') === 0) {
  766. $request_uri = $request->getUri();
  767. }
  768. // @todo Remove this parsing once these are removed from the request in
  769. // https://www.drupal.org/node/2504709.
  770. $parsed = UrlHelper::parse($request_uri);
  771. unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]);
  772. return $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : '');
  773. }
  774. /**
  775. * {@inheritdoc}
  776. */
  777. public function setInvalidTokenError(FormStateInterface $form_state) {
  778. $this->formValidator->setInvalidTokenError($form_state);
  779. }
  780. /**
  781. * {@inheritdoc}
  782. */
  783. public function validateForm($form_id, &$form, FormStateInterface &$form_state) {
  784. $this->formValidator->validateForm($form_id, $form, $form_state);
  785. }
  786. /**
  787. * {@inheritdoc}
  788. */
  789. public function redirectForm(FormStateInterface $form_state) {
  790. return $this->formSubmitter->redirectForm($form_state);
  791. }
  792. /**
  793. * {@inheritdoc}
  794. */
  795. public function executeValidateHandlers(&$form, FormStateInterface &$form_state) {
  796. $this->formValidator->executeValidateHandlers($form, $form_state);
  797. }
  798. /**
  799. * {@inheritdoc}
  800. */
  801. public function executeSubmitHandlers(&$form, FormStateInterface &$form_state) {
  802. $this->formSubmitter->executeSubmitHandlers($form, $form_state);
  803. }
  804. /**
  805. * {@inheritdoc}
  806. */
  807. public function doSubmitForm(&$form, FormStateInterface &$form_state) {
  808. throw new \LogicException('Use FormBuilderInterface::processForm() instead.');
  809. }
  810. /**
  811. * {@inheritdoc}
  812. */
  813. public function doBuildForm($form_id, &$element, FormStateInterface &$form_state) {
  814. // Initialize as unprocessed.
  815. $element['#processed'] = FALSE;
  816. // Use element defaults.
  817. if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element['#type']))) {
  818. // Overlay $info onto $element, retaining preexisting keys in $element.
  819. $element += $info;
  820. $element['#defaults_loaded'] = TRUE;
  821. }
  822. // Assign basic defaults common for all form elements.
  823. $element += [
  824. '#required' => FALSE,
  825. '#attributes' => [],
  826. '#title_display' => 'before',
  827. '#description_display' => 'after',
  828. '#errors' => NULL,
  829. ];
  830. // Special handling if we're on the top level form element.
  831. if (isset($element['#type']) && $element['#type'] == 'form') {
  832. if (!empty($element['#https']) && !UrlHelper::isExternal($element['#action'])) {
  833. global $base_root;
  834. // Not an external URL so ensure that it is secure.
  835. $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
  836. }
  837. // Store a reference to the complete form in $form_state prior to building
  838. // the form. This allows advanced #process and #after_build callbacks to
  839. // perform changes elsewhere in the form.
  840. $form_state->setCompleteForm($element);
  841. // Set a flag if we have a correct form submission. This is always TRUE
  842. // for programmed forms coming from self::submitForm(), or if the form_id
  843. // coming from the POST data is set and matches the current form_id.
  844. $input = $form_state->getUserInput();
  845. if ($form_state->isProgrammed() || (!empty($input) && (isset($input['form_id']) && ($input['form_id'] == $form_id)))) {
  846. $form_state->setProcessInput();
  847. if (isset($element['#token'])) {
  848. $input = $form_state->getUserInput();
  849. if (empty($input['form_token']) || !$this->csrfToken->validate($input['form_token'], $element['#token'])) {
  850. // Set an early form error to block certain input processing since
  851. // that opens the door for CSRF vulnerabilities.
  852. $this->setInvalidTokenError($form_state);
  853. // This value is checked in self::handleInputElement().
  854. $form_state->setInvalidToken(TRUE);
  855. // Make sure file uploads do not get processed.
  856. $this->requestStack->getCurrentRequest()->files = new FileBag();
  857. }
  858. }
  859. }
  860. else {
  861. $form_state->setProcessInput(FALSE);
  862. }
  863. // All form elements should have an #array_parents property.
  864. $element['#array_parents'] = [];
  865. }
  866. if (!isset($element['#id'])) {
  867. $unprocessed_id = 'edit-' . implode('-', $element['#parents']);
  868. $element['#id'] = Html::getUniqueId($unprocessed_id);
  869. // Provide a selector usable by JavaScript. As the ID is unique, its not
  870. // possible to rely on it in JavaScript.
  871. $element['#attributes']['data-drupal-selector'] = Html::getId($unprocessed_id);
  872. }
  873. else {
  874. // Provide a selector usable by JavaScript. As the ID is unique, its not
  875. // possible to rely on it in JavaScript.
  876. $element['#attributes']['data-drupal-selector'] = Html::getId($element['#id']);
  877. }
  878. // Add the aria-describedby attribute to associate the form control with its
  879. // description.
  880. if (!empty($element['#description'])) {
  881. $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
  882. }
  883. // Handle input elements.
  884. if (!empty($element['#input'])) {
  885. $this->handleInputElement($form_id, $element, $form_state);
  886. }
  887. // Allow for elements to expand to multiple elements, e.g., radios,
  888. // checkboxes and files.
  889. if (isset($element['#process']) && !$element['#processed']) {
  890. foreach ($element['#process'] as $callback) {
  891. $complete_form = &$form_state->getCompleteForm();
  892. $element = call_user_func_array($form_state->prepareCallback($callback), [&$element, &$form_state, &$complete_form]);
  893. }
  894. $element['#processed'] = TRUE;
  895. }
  896. // We start off assuming all form elements are in the correct order.
  897. $element['#sorted'] = TRUE;
  898. // Recurse through all child elements.
  899. $count = 0;
  900. if (isset($element['#access'])) {
  901. $access = $element['#access'];
  902. $inherited_access = NULL;
  903. if (($access instanceof AccessResultInterface && !$access->isAllowed()) || $access === FALSE) {
  904. $inherited_access = $access;
  905. }
  906. }
  907. foreach (Element::children($element) as $key) {
  908. // Prior to checking properties of child elements, their default
  909. // properties need to be loaded.
  910. if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element[$key]['#type']))) {
  911. $element[$key] += $info;
  912. $element[$key]['#defaults_loaded'] = TRUE;
  913. }
  914. // Don't squash an existing tree value.
  915. if (!isset($element[$key]['#tree'])) {
  916. $element[$key]['#tree'] = $element['#tree'];
  917. }
  918. // Children inherit #access from parent.
  919. if (isset($inherited_access)) {
  920. $element[$key]['#access'] = $inherited_access;
  921. }
  922. // Make child elements inherit their parent's #disabled and #allow_focus
  923. // values unless they specify their own.
  924. foreach (['#disabled', '#allow_focus'] as $property) {
  925. if (isset($element[$property]) && !isset($element[$key][$property])) {
  926. $element[$key][$property] = $element[$property];
  927. }
  928. }
  929. // Don't squash existing parents value.
  930. if (!isset($element[$key]['#parents'])) {
  931. // Check to see if a tree of child elements is present. If so,
  932. // continue down the tree if required.
  933. $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], [$key]) : [$key];
  934. }
  935. // Ensure #array_parents follows the actual form structure.
  936. $array_parents = $element['#array_parents'];
  937. $array_parents[] = $key;
  938. $element[$key]['#array_parents'] = $array_parents;
  939. // Assign a decimal placeholder weight to preserve original array order.
  940. if (!isset($element[$key]['#weight'])) {
  941. $element[$key]['#weight'] = $count / 1000;
  942. }
  943. else {
  944. // If one of the child elements has a weight then we will need to sort
  945. // later.
  946. unset($element['#sorted']);
  947. }
  948. $element[$key] = $this->doBuildForm($form_id, $element[$key], $form_state);
  949. $count++;
  950. }
  951. // The #after_build flag allows any piece of a form to be altered
  952. // after normal input parsing has been completed.
  953. if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
  954. foreach ($element['#after_build'] as $callback) {
  955. $element = call_user_func_array($form_state->prepareCallback($callback), [$element, &$form_state]);
  956. }
  957. $element['#after_build_done'] = TRUE;
  958. }
  959. // If there is a file element, we need to flip a flag so later the
  960. // form encoding can be set.
  961. if (isset($element['#type']) && $element['#type'] == 'file') {
  962. $form_state->setHasFileElement();
  963. }
  964. // Final tasks for the form element after self::doBuildForm() has run for
  965. // all other elements.
  966. if (isset($element['#type']) && $element['#type'] == 'form') {
  967. // If there is a file element, we set the form encoding.
  968. if ($form_state->hasFileElement()) {
  969. $element['#attributes']['enctype'] = 'multipart/form-data';
  970. }
  971. // Allow Ajax submissions to the form action to bypass verification. This
  972. // is especially useful for multipart forms, which cannot be verified via
  973. // a response header.
  974. $element['#attached']['drupalSettings']['ajaxTrustedUrl'][$element['#action']] = TRUE;
  975. // If a form contains a single textfield, and the ENTER key is pressed
  976. // within it, Internet Explorer submits the form with no POST data
  977. // identifying any submit button. Other browsers submit POST data as
  978. // though the user clicked the first button. Therefore, to be as
  979. // consistent as we can be across browsers, if no 'triggering_element' has
  980. // been identified yet, default it to the first button.
  981. $buttons = $form_state->getButtons();
  982. if (!$form_state->isProgrammed() && !$form_state->getTriggeringElement() && !empty($buttons)) {
  983. $form_state->setTriggeringElement($buttons[0]);
  984. }
  985. $triggering_element = $form_state->getTriggeringElement();
  986. // If the triggering element specifies "button-level" validation and
  987. // submit handlers to run instead of the default form-level ones, then add
  988. // those to the form state.
  989. if (isset($triggering_element['#validate'])) {
  990. $form_state->setValidateHandlers($triggering_element['#validate']);
  991. }
  992. if (isset($triggering_element['#submit'])) {
  993. $form_state->setSubmitHandlers($triggering_element['#submit']);
  994. }
  995. // If the triggering element executes submit handlers, then set the form
  996. // state key that's needed for those handlers to run.
  997. if (!empty($triggering_element['#executes_submit_callback'])) {
  998. $form_state->setSubmitted();
  999. }
  1000. // Special processing if the triggering element is a button.
  1001. if (!empty($triggering_element['#is_button'])) {
  1002. // Because there are several ways in which the triggering element could
  1003. // have been determined (including from input variables set by
  1004. // JavaScript or fallback behavior implemented for IE), and because
  1005. // buttons often have their #name property not derived from their
  1006. // #parents property, we can't assume that input processing that's
  1007. // happened up until here has resulted in
  1008. // $form_state->getValue(BUTTON_NAME) being set. But it's common for
  1009. // forms to have several buttons named 'op' and switch on
  1010. // $form_state->getValue('op') during submit handler execution.
  1011. $form_state->setValue($triggering_element['#name'], $triggering_element['#value']);
  1012. }
  1013. }
  1014. return $element;
  1015. }
  1016. /**
  1017. * Helper function to normalize the different callable formats.
  1018. *
  1019. * @param callable $value_callable
  1020. * The callable to be checked.
  1021. *
  1022. * @return bool
  1023. * TRUE if the callable is safe even if the CSRF token is invalid, FALSE
  1024. * otherwise.
  1025. */
  1026. protected function valueCallableIsSafe(callable $value_callable) {
  1027. // The same static class method callable may be formatted in two array and
  1028. // two string forms:
  1029. // ['\Classname', 'methodname']
  1030. // ['Classname', 'methodname']
  1031. // '\Classname::methodname'
  1032. // 'Classname::methodname'
  1033. if (is_callable($value_callable, FALSE, $callable_name)) {
  1034. // The third parameter of is_callable() is set to a string form, but we
  1035. // still have to normalize further by stripping a leading '\'.
  1036. return in_array(ltrim($callable_name, '\\'), $this->safeCoreValueCallables);
  1037. }
  1038. return FALSE;
  1039. }
  1040. /**
  1041. * Adds the #name and #value properties of an input element before rendering.
  1042. */
  1043. protected function handleInputElement($form_id, &$element, FormStateInterface &$form_state) {
  1044. if (!isset($element['#name'])) {
  1045. $name = array_shift($element['#parents']);
  1046. $element['#name'] = $name;
  1047. if ($element['#type'] == 'file') {
  1048. // To make it easier to handle files in file.inc, we place all
  1049. // file fields in the 'files' array. Also, we do not support
  1050. // nested file names.
  1051. // @todo Remove this files prefix now?
  1052. $element['#name'] = 'files[' . $element['#name'] . ']';
  1053. }
  1054. elseif (count($element['#parents'])) {
  1055. $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
  1056. }
  1057. array_unshift($element['#parents'], $name);
  1058. }
  1059. // Setting #disabled to TRUE results in user input being ignored regardless
  1060. // of how the element is themed or whether JavaScript is used to change the
  1061. // control's attributes. However, it's good UI to let the user know that
  1062. // input is not wanted for the control. HTML supports two attributes for:
  1063. // this: http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form
  1064. // wants to start a control off with one of these attributes for UI
  1065. // purposes, only, but still allow input to be processed if it's submitted,
  1066. // it can set the desired attribute in #attributes directly rather than
  1067. // using #disabled. However, developers should think carefully about the
  1068. // accessibility implications of doing so: if the form expects input to be
  1069. // enterable under some condition triggered by JavaScript, how would someone
  1070. // who has JavaScript disabled trigger that condition? Instead, developers
  1071. // should consider whether a multi-step form would be more appropriate
  1072. // (#disabled can be changed from step to step). If one still decides to use
  1073. // JavaScript to affect when a control is enabled, then it is best for
  1074. // accessibility for the control to be enabled in the HTML, and disabled by
  1075. // JavaScript on document ready.
  1076. if (!empty($element['#disabled'])) {
  1077. if (!empty($element['#allow_focus'])) {
  1078. $element['#attributes']['readonly'] = 'readonly';
  1079. }
  1080. else {
  1081. $element['#attributes']['disabled'] = 'disabled';
  1082. }
  1083. }
  1084. // With JavaScript or other easy hacking, input can be submitted even for
  1085. // elements with #access=FALSE or #disabled=TRUE. For security, these must
  1086. // not be processed. Forms that set #disabled=TRUE on an element do not
  1087. // expect input for the element, and even forms submitted with
  1088. // self::submitForm() must not be able to get around this. Forms that set
  1089. // #access=FALSE on an element usually allow access for some users, so forms
  1090. // submitted with self::submitForm() may bypass access restriction and be
  1091. // treated as high-privilege users instead.
  1092. $process_input = empty($element['#disabled']) && (($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks()) || ($form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access'])));
  1093. // Set the element's #value property.
  1094. if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
  1095. // @todo Once all elements are converted to plugins in
  1096. // https://www.drupal.org/node/2311393, rely on
  1097. // $element['#value_callback'] directly.
  1098. $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
  1099. if (!is_callable($value_callable)) {
  1100. $value_callable = '\Drupal\Core\Render\Element\FormElement::valueCallback';
  1101. }
  1102. if ($process_input) {
  1103. // Get the input for the current element. NULL values in the input need
  1104. // to be explicitly distinguished from missing input. (see below)
  1105. $input_exists = NULL;
  1106. $input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'], $input_exists);
  1107. // For browser-submitted forms, the submitted values do not contain
  1108. // values for certain elements (empty multiple select, unchecked
  1109. // checkbox). During initial form processing, we add explicit NULL
  1110. // values for such elements in FormState::$input. When rebuilding the
  1111. // form, we can distinguish elements having NULL input from elements
  1112. // that were not part of the initially submitted form and can therefore
  1113. // use default values for the latter, if required. Programmatically
  1114. // submitted forms can submit explicit NULL values when calling
  1115. // self::submitForm() so we do not modify FormState::$input for them.
  1116. if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) {
  1117. // Add the necessary parent keys to FormState::$input and sets the
  1118. // element's input value to NULL.
  1119. NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
  1120. $input_exists = TRUE;
  1121. }
  1122. // If we have input for the current element, assign it to the #value
  1123. // property, optionally filtered through $value_callback.
  1124. if ($input_exists) {
  1125. // Skip all value callbacks except safe ones like text if the CSRF
  1126. // token was invalid.
  1127. if (!$form_state->hasInvalidToken() || $this->valueCallableIsSafe($value_callable)) {
  1128. $element['#value'] = call_user_func_array($value_callable, [&$element, $input, &$form_state]);
  1129. }
  1130. else {
  1131. $input = NULL;
  1132. }
  1133. if (!isset($element['#value']) && isset($input)) {
  1134. $element['#value'] = $input;
  1135. }
  1136. }
  1137. // Mark all posted values for validation.
  1138. if (isset($element['#value']) || (!empty($element['#required']))) {
  1139. $element['#needs_validation'] = TRUE;
  1140. }
  1141. }
  1142. // Load defaults.
  1143. if (!isset($element['#value'])) {
  1144. // Call #type_value without a second argument to request default_value
  1145. // handling.
  1146. $element['#value'] = call_user_func_array($value_callable, [&$element, FALSE, &$form_state]);
  1147. // Final catch. If we haven't set a value yet, use the explicit default
  1148. // value. Avoid image buttons (which come with garbage value), so we
  1149. // only get value for the button actually clicked.
  1150. if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
  1151. $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
  1152. }
  1153. }
  1154. }
  1155. // Determine which element (if any) triggered the submission of the form and
  1156. // keep track of all the clickable buttons in the form for
  1157. // \Drupal\Core\Form\FormState::cleanValues(). Enforce the same input
  1158. // processing restrictions as above.
  1159. if ($process_input) {
  1160. // Detect if the element triggered the submission via Ajax.
  1161. if ($this->elementTriggeredScriptedSubmission($element, $form_state)) {
  1162. $form_state->setTriggeringElement($element);
  1163. }
  1164. // If the form was submitted by the browser rather than via Ajax, then it
  1165. // can only have been triggered by a button, and we need to determine
  1166. // which button within the constraints of how browsers provide this
  1167. // information.
  1168. if (!empty($element['#is_button'])) {
  1169. // All buttons in the form need to be tracked for
  1170. // \Drupal\Core\Form\FormState::cleanValues() and for the
  1171. // self::doBuildForm() code that handles a form submission containing no
  1172. // button information in \Drupal::request()->request.
  1173. $buttons = $form_state->getButtons();
  1174. $buttons[] = $element;
  1175. $form_state->setButtons($buttons);
  1176. if ($this->buttonWasClicked($element, $form_state)) {
  1177. $form_state->setTriggeringElement($element);
  1178. }
  1179. }
  1180. }
  1181. // Set the element's value in $form_state->getValues(), but only, if its key
  1182. // does not exist yet (a #value_callback may have already populated it).
  1183. if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) {
  1184. $form_state->setValueForElement($element, $element['#value']);
  1185. }
  1186. }
  1187. /**
  1188. * Detects if an element triggered the form submission via Ajax.
  1189. *
  1190. * This detects button or non-button controls that trigger a form submission
  1191. * via Ajax or some other scriptable environment. These environments can set
  1192. * the special input key '_triggering_element_name' to identify the triggering
  1193. * element. If the name alone doesn't identify the element uniquely, the input
  1194. * key '_triggering_element_value' may also be set to require a match on
  1195. * element value. An example where this is needed is if there are several
  1196. * // buttons all named 'op', and only differing in their value.
  1197. */
  1198. protected function elementTriggeredScriptedSubmission($element, FormStateInterface &$form_state) {
  1199. $input = $form_state->getUserInput();
  1200. if (!empty($input['_triggering_element_name']) && $element['#name'] == $input['_triggering_element_name']) {
  1201. if (empty($input['_triggering_element_value']) || $input['_triggering_element_value'] == $element['#value']) {
  1202. return TRUE;
  1203. }
  1204. }
  1205. return FALSE;
  1206. }
  1207. /**
  1208. * Determines if a given button triggered the form submission.
  1209. *
  1210. * This detects button controls that trigger a form submission by being
  1211. * clicked and having the click processed by the browser rather than being
  1212. * captured by JavaScript. Essentially, it detects if the button's name and
  1213. * value are part of the POST data, but with extra code to deal with the
  1214. * convoluted way in which browsers submit data for image button clicks.
  1215. *
  1216. * This does not detect button clicks processed by Ajax (that is done in
  1217. * self::elementTriggeredScriptedSubmission()) and it does not detect form
  1218. * submissions from Internet Explorer in response to an ENTER key pressed in a
  1219. * textfield (self::doBuildForm() has extra code for that).
  1220. *
  1221. * Because this function contains only part of the logic needed to determine
  1222. * $form_state->getTriggeringElement(), it should not be called from anywhere
  1223. * other than within the Form API. Form validation and submit handlers needing
  1224. * to know which button was clicked should get that information from
  1225. * $form_state->getTriggeringElement().
  1226. */
  1227. protected function buttonWasClicked($element, FormStateInterface &$form_state) {
  1228. // First detect normal 'vanilla' button clicks. Traditionally, all standard
  1229. // buttons on a form share the same name (usually 'op'), and the specific
  1230. // return value is used to determine which was clicked. This ONLY works as
  1231. // long as $form['#name'] puts the value at the top level of the tree of
  1232. // \Drupal::request()->request data.
  1233. $input = $form_state->getUserInput();
  1234. // The input value attribute is treated as CDATA by browsers. This means
  1235. // that they replace character entities with characters. Therefore, we need
  1236. // to decode the value in $element['#value']. For more details see
  1237. // http://www.w3.org/TR/html401/types.html#type-cdata.
  1238. if (isset($input[$element['#name']]) && $input[$element['#name']] == Html::decodeEntities($element['#value'])) {
  1239. return TRUE;
  1240. }
  1241. // When image buttons are clicked, browsers do NOT pass the form element
  1242. // value in \Drupal::request()->Request. Instead they pass an integer
  1243. // representing the coordinates of the click on the button image. This means
  1244. // that image buttons MUST have unique $form['#name'] values, but the
  1245. // details of their \Drupal::request()->request data should be ignored.
  1246. elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
  1247. return TRUE;
  1248. }
  1249. return FALSE;
  1250. }
  1251. /**
  1252. * Wraps file_upload_max_size().
  1253. *
  1254. * @return string
  1255. * A translated string representation of the size of the file size limit
  1256. * based on the PHP upload_max_filesize and post_max_size.
  1257. */
  1258. protected function getFileUploadMaxSize() {
  1259. return file_upload_max_size();
  1260. }
  1261. /**
  1262. * Gets the current active user.
  1263. *
  1264. * @return \Drupal\Core\Session\AccountInterface
  1265. */
  1266. protected function currentUser() {
  1267. if (!$this->currentUser && \Drupal::hasService('current_user')) {
  1268. $this->currentUser = \Drupal::currentUser();
  1269. }
  1270. return $this->currentUser;
  1271. }
  1272. }