0e49c5bb085cf301d6e8a60b6727c5992ea7fc5f
[yaffs-website] / web / core / lib / Drupal / Core / Form / EventSubscriber / FormAjaxSubscriber.php
1 <?php
2
3 namespace Drupal\Core\Form\EventSubscriber;
4
5 use Drupal\Core\Ajax\AjaxResponse;
6 use Drupal\Core\Ajax\PrependCommand;
7 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
8 use Drupal\Core\Form\Exception\BrokenPostRequestException;
9 use Drupal\Core\Form\FormAjaxException;
10 use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
11 use Drupal\Core\Form\FormBuilderInterface;
12 use Drupal\Core\StringTranslation\StringTranslationTrait;
13 use Drupal\Core\StringTranslation\TranslationInterface;
14 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
16 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
17 use Symfony\Component\HttpKernel\KernelEvents;
18
19 /**
20  * Wraps AJAX form submissions that are triggered via an exception.
21  */
22 class FormAjaxSubscriber implements EventSubscriberInterface {
23
24   use StringTranslationTrait;
25
26   /**
27    * The form AJAX response builder.
28    *
29    * @var \Drupal\Core\Form\FormAjaxResponseBuilderInterface
30    */
31   protected $formAjaxResponseBuilder;
32
33   /**
34    * Constructs a new FormAjaxSubscriber.
35    *
36    * @param \Drupal\Core\Form\FormAjaxResponseBuilderInterface $form_ajax_response_builder
37    *   The form AJAX response builder.
38    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
39    *   The string translation.
40    */
41   public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder, TranslationInterface $string_translation) {
42     $this->formAjaxResponseBuilder = $form_ajax_response_builder;
43     $this->stringTranslation = $string_translation;
44   }
45
46   /**
47    * Alters the wrapper format if this is an AJAX form request.
48    *
49    * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
50    *   The event to process.
51    */
52   public function onView(GetResponseForControllerResultEvent $event) {
53     // To support an AJAX form submission of a form within a block, make the
54     // later VIEW subscribers process the controller result as though for
55     // HTML display (i.e., add blocks). During that block building, when the
56     // submitted form gets processed, an exception gets thrown by
57     // \Drupal\Core\Form\FormBuilderInterface::buildForm(), allowing
58     // self::onException() to return an AJAX response instead of an HTML one.
59     $request = $event->getRequest();
60     if ($request->query->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
61       $request->query->set(MainContentViewSubscriber::WRAPPER_FORMAT, 'html');
62     }
63   }
64
65   /**
66    * Catches a form AJAX exception and build a response from it.
67    *
68    * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
69    *   The event to process.
70    */
71   public function onException(GetResponseForExceptionEvent $event) {
72     $exception = $event->getException();
73     $request = $event->getRequest();
74
75     // Render a nice error message in case we have a file upload which exceeds
76     // the configured upload limit.
77     if ($exception instanceof BrokenPostRequestException && $request->query->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
78       $this->drupalSetMessage($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]), 'error');
79       $response = new AjaxResponse(NULL, 200);
80       $status_messages = ['#type' => 'status_messages'];
81       $response->addCommand(new PrependCommand(NULL, $status_messages));
82       $event->allowCustomResponseCode();
83       $event->setResponse($response);
84       return;
85     }
86
87     // Extract the form AJAX exception (it may have been passed to another
88     // exception before reaching here).
89     if ($exception = $this->getFormAjaxException($exception)) {
90       $request = $event->getRequest();
91       $form = $exception->getForm();
92       $form_state = $exception->getFormState();
93
94       // Set the build ID from the request as the old build ID on the form.
95       $form['#build_id_old'] = $request->request->get('form_build_id');
96
97       try {
98         $response = $this->formAjaxResponseBuilder->buildResponse($request, $form, $form_state, []);
99
100         // Since this response is being set in place of an exception, explicitly
101         // mark this as a 200 status.
102         $response->setStatusCode(200);
103         $event->allowCustomResponseCode();
104         $event->setResponse($response);
105       }
106       catch (\Exception $e) {
107         // Otherwise, replace the existing exception with the new one.
108         $event->setException($e);
109       }
110     }
111   }
112
113   /**
114    * Extracts a form AJAX exception.
115    *
116    * @param \Exception $e
117    *   A generic exception that might contain a form AJAX exception.
118    *
119    * @return \Drupal\Core\Form\FormAjaxException|null
120    *   Either the form AJAX exception, or NULL if none could be found.
121    */
122   protected function getFormAjaxException(\Exception $e) {
123     $exception = NULL;
124     while ($e) {
125       if ($e instanceof FormAjaxException) {
126         $exception = $e;
127         break;
128       }
129
130       $e = $e->getPrevious();
131     }
132     return $exception;
133   }
134
135   /**
136    * Wraps format_size()
137    *
138    * @return string
139    *   The formatted size.
140    */
141   protected function formatSize($size) {
142     return format_size($size);
143   }
144
145   /**
146    * {@inheritdoc}
147    */
148   public static function getSubscribedEvents() {
149     // Run before exception.logger.
150     $events[KernelEvents::EXCEPTION] = ['onException', 51];
151     // Run before main_content_view_subscriber.
152     $events[KernelEvents::VIEW][] = ['onView', 1];
153
154     return $events;
155   }
156
157   /**
158    * Wraps drupal_set_message().
159    *
160    * @codeCoverageIgnore
161    */
162   protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
163     drupal_set_message($message, $type, $repeat);
164   }
165
166 }