annotate core/lib/Drupal/Core/EventSubscriber/AjaxResponseSubscriber.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
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 }