Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Core\EventSubscriber;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Core\Ajax\AjaxResponse;
|
Chris@0
|
7 use Drupal\Core\Render\AttachmentsResponseProcessorInterface;
|
Chris@0
|
8 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
Chris@0
|
9 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
Chris@0
|
10 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
Chris@0
|
11 use Symfony\Component\HttpKernel\KernelEvents;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Response subscriber to handle AJAX responses.
|
Chris@0
|
15 */
|
Chris@0
|
16 class AjaxResponseSubscriber implements EventSubscriberInterface {
|
Chris@0
|
17
|
Chris@0
|
18 /**
|
Chris@0
|
19 * The AJAX response attachments processor service.
|
Chris@0
|
20 *
|
Chris@0
|
21 * @var \Drupal\Core\Render\AttachmentsResponseProcessorInterface
|
Chris@0
|
22 */
|
Chris@0
|
23 protected $ajaxResponseAttachmentsProcessor;
|
Chris@0
|
24
|
Chris@0
|
25 /**
|
Chris@0
|
26 * Constructs an AjaxResponseSubscriber object.
|
Chris@0
|
27 *
|
Chris@0
|
28 * @param \Drupal\Core\Render\AttachmentsResponseProcessorInterface $ajax_response_attachments_processor
|
Chris@0
|
29 * The AJAX response attachments processor service.
|
Chris@0
|
30 */
|
Chris@0
|
31 public function __construct(AttachmentsResponseProcessorInterface $ajax_response_attachments_processor) {
|
Chris@0
|
32 $this->ajaxResponseAttachmentsProcessor = $ajax_response_attachments_processor;
|
Chris@0
|
33 }
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * Request parameter to indicate that a request is a Drupal Ajax request.
|
Chris@0
|
37 */
|
Chris@0
|
38 const AJAX_REQUEST_PARAMETER = '_drupal_ajax';
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * Sets the AJAX parameter from the current request.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
Chris@0
|
44 * The response event, which contains the current request.
|
Chris@0
|
45 */
|
Chris@0
|
46 public function onRequest(GetResponseEvent $event) {
|
Chris@0
|
47 // Pass to the Html class that the current request is an Ajax request.
|
Chris@0
|
48 if ($event->getRequest()->request->get(static::AJAX_REQUEST_PARAMETER)) {
|
Chris@0
|
49 Html::setIsAjax(TRUE);
|
Chris@0
|
50 }
|
Chris@0
|
51 }
|
Chris@0
|
52
|
Chris@0
|
53 /**
|
Chris@0
|
54 * Renders the ajax commands right before preparing the result.
|
Chris@0
|
55 *
|
Chris@0
|
56 * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
Chris@0
|
57 * The response event, which contains the possible AjaxResponse object.
|
Chris@0
|
58 */
|
Chris@0
|
59 public function onResponse(FilterResponseEvent $event) {
|
Chris@0
|
60 $response = $event->getResponse();
|
Chris@0
|
61 if ($response instanceof AjaxResponse) {
|
Chris@0
|
62 $this->ajaxResponseAttachmentsProcessor->processAttachments($response);
|
Chris@0
|
63
|
Chris@0
|
64 // IE 9 does not support XHR 2 (http://caniuse.com/#feat=xhr2), so
|
Chris@0
|
65 // for that browser, jquery.form submits requests containing a file upload
|
Chris@0
|
66 // via an IFRAME rather than via XHR. Since the response is being sent to
|
Chris@0
|
67 // an IFRAME, it must be formatted as HTML. Specifically:
|
Chris@0
|
68 // - It must use the text/html content type or else the browser will
|
Chris@0
|
69 // present a download prompt. Note: This applies to both file uploads
|
Chris@0
|
70 // as well as any ajax request in a form with a file upload form.
|
Chris@0
|
71 // - It must place the JSON data into a textarea to prevent browser
|
Chris@0
|
72 // extensions such as Linkification and Skype's Browser Highlighter
|
Chris@0
|
73 // from applying HTML transformations such as URL or phone number to
|
Chris@0
|
74 // link conversions on the data values.
|
Chris@0
|
75 //
|
Chris@0
|
76 // Since this affects the format of the output, it could be argued that
|
Chris@0
|
77 // this should be implemented as a separate Accept MIME type. However,
|
Chris@0
|
78 // that would require separate variants for each type of AJAX request
|
Chris@0
|
79 // (e.g., drupal-ajax, drupal-dialog, drupal-modal), so for expediency,
|
Chris@0
|
80 // this browser workaround is implemented via a GET or POST parameter.
|
Chris@0
|
81 //
|
Chris@0
|
82 // @see http://malsup.com/jquery/form/#file-upload
|
Chris@0
|
83 // @see https://www.drupal.org/node/1009382
|
Chris@0
|
84 // @see https://www.drupal.org/node/2339491
|
Chris@0
|
85 // @see Drupal.ajax.prototype.beforeSend()
|
Chris@0
|
86 $accept = $event->getRequest()->headers->get('accept');
|
Chris@0
|
87
|
Chris@0
|
88 if (strpos($accept, 'text/html') !== FALSE) {
|
Chris@0
|
89 $response->headers->set('Content-Type', 'text/html; charset=utf-8');
|
Chris@0
|
90
|
Chris@0
|
91 // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
|
Chris@0
|
92 // and Skype's Browser Highlighter, convert URLs, phone numbers, etc.
|
Chris@0
|
93 // into links. This corrupts the JSON response. Protect the integrity of
|
Chris@0
|
94 // the JSON data by making it the value of a textarea.
|
Chris@0
|
95 // @see http://malsup.com/jquery/form/#file-upload
|
Chris@0
|
96 // @see https://www.drupal.org/node/1009382
|
Chris@0
|
97 $response->setContent('<textarea>' . $response->getContent() . '</textarea>');
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 // User-uploaded files cannot set any response headers, so a custom header
|
Chris@0
|
101 // is used to indicate to ajax.js that this response is safe. Note that
|
Chris@0
|
102 // most Ajax requests bound using the Form API will be protected by having
|
Chris@0
|
103 // the URL flagged as trusted in Drupal.settings, so this header is used
|
Chris@0
|
104 // only for things like custom markup that gets Ajax behaviors attached.
|
Chris@0
|
105 $response->headers->set('X-Drupal-Ajax-Token', 1);
|
Chris@0
|
106 }
|
Chris@0
|
107 }
|
Chris@0
|
108
|
Chris@0
|
109 /**
|
Chris@0
|
110 * {@inheritdoc}
|
Chris@0
|
111 */
|
Chris@0
|
112 public static function getSubscribedEvents() {
|
Chris@0
|
113 $events[KernelEvents::RESPONSE][] = ['onResponse', -100];
|
Chris@0
|
114 $events[KernelEvents::REQUEST][] = ['onRequest', 50];
|
Chris@0
|
115
|
Chris@0
|
116 return $events;
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 }
|