Commit e4e74fcf authored by alexpott's avatar alexpott

Issue #1959574 by dawehner, mkadin, Wim Leers, jessebeach: Remove the deprecated Drupal 7 Ajax API.

parent c3b12738
...@@ -410,13 +410,15 @@ services: ...@@ -410,13 +410,15 @@ services:
arguments: ['@controller_resolver', '@string_translation', '@title_resolver'] arguments: ['@controller_resolver', '@string_translation', '@title_resolver']
controller.ajax: controller.ajax:
class: Drupal\Core\Controller\AjaxController class: Drupal\Core\Controller\AjaxController
arguments: ['@controller_resolver'] arguments: ['@controller_resolver', '@ajax_response_renderer']
controller.entityform: controller.entityform:
class: Drupal\Core\Entity\HtmlEntityFormController class: Drupal\Core\Entity\HtmlEntityFormController
arguments: ['@controller_resolver', '@service_container', '@entity.manager'] arguments: ['@controller_resolver', '@service_container', '@entity.manager']
controller.dialog: controller.dialog:
class: Drupal\Core\Controller\DialogController class: Drupal\Core\Controller\DialogController
arguments: ['@controller_resolver', '@title_resolver'] arguments: ['@controller_resolver', '@title_resolver']
ajax_response_renderer:
class: Drupal\Core\Ajax\AjaxResponseRenderer
router_listener: router_listener:
class: Symfony\Component\HttpKernel\EventListener\RouterListener class: Symfony\Component\HttpKernel\EventListener\RouterListener
tags: tags:
...@@ -428,7 +430,7 @@ services: ...@@ -428,7 +430,7 @@ services:
class: Drupal\Core\EventSubscriber\ViewSubscriber class: Drupal\Core\EventSubscriber\ViewSubscriber
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
arguments: ['@content_negotiation', '@title_resolver'] arguments: ['@content_negotiation', '@title_resolver', '@ajax_response_renderer']
html_view_subscriber: html_view_subscriber:
class: Drupal\Core\EventSubscriber\HtmlViewSubscriber class: Drupal\Core\EventSubscriber\HtmlViewSubscriber
tags: tags:
......
This diff is collapsed.
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\AjaxResponseRenderer.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Page\HtmlFragment;
use Symfony\Component\HttpFoundation\Response;
/**
* Converts a controller result into an Ajax response object.
*/
class AjaxResponseRenderer {
/**
* Converts the output of a controller into an Ajax response object.
*
* @var mixed $content
* The return value of a controller, for example a string, a render array, a
* HtmlFragment object, a Response object or even an AjaxResponse itself.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An Ajax response containing the controller result.
*/
public function render($content) {
// If there is already an AjaxResponse, then return it without manipulation.
if ($content instanceof AjaxResponse && $content->isOk()) {
return $content;
}
// Allow controllers to return a HtmlFragment or a Response object directly.
if ($content instanceof HtmlFragment) {
$content = $content->getContent();
}
elseif ($content instanceof Response) {
$content = $content->getContent();
}
// Most controllers return a render array, but some return a string.
if (!is_array($content)) {
$content = array(
'#markup' => $content,
);
}
$response = new AjaxResponse();
if (isset($content['#type']) && ($content['#type'] == 'ajax')) {
// Complex Ajax callbacks can return a result that contains an error
// message or a specific set of commands to send to the browser.
$content += element_info('ajax');
$error = $content['#error'];
if (!empty($error)) {
// Fall back to some default message otherwise use the specific one.
if (!is_string($error)) {
$error = 'An error occurred while handling the request: The server received invalid input.';
}
$response->addCommand(new AlertCommand($error));
}
}
$html = drupal_render($content);
// The selector for the insert command is NULL as the new content will
// replace the element making the Ajax call. The default 'replaceWith'
// behavior can be changed with #ajax['method'].
$response->addCommand(new InsertCommand(NULL, $html));
$status_messages = array('#theme' => 'status_messages');
$output = drupal_render($status_messages);
if (!empty($output)) {
$response->addCommand(new PrependCommand(NULL, $output));
}
return $response;
}
}
...@@ -7,17 +7,12 @@ ...@@ -7,17 +7,12 @@
namespace Drupal\Core\Controller; namespace Drupal\Core\Controller;
use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\AjaxResponseRenderer;
use Drupal\Core\Ajax\InsertCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Page\HtmlFragment;
use Drupal\Core\Page\HtmlPage;
use Symfony\Component\DependencyInjection\ContainerAware; use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/** /**
* Default controller for ajax requests. * Default controller for Ajax requests.
*/ */
class AjaxController extends ContainerAware { class AjaxController extends ContainerAware {
...@@ -28,63 +23,40 @@ class AjaxController extends ContainerAware { ...@@ -28,63 +23,40 @@ class AjaxController extends ContainerAware {
*/ */
protected $controllerResolver; protected $controllerResolver;
/**
* The Ajax response renderer.
*
* @var \Drupal\Core\Ajax\AjaxResponseRenderer
*/
protected $ajaxRenderer;
/** /**
* Constructs a new AjaxController instance. * Constructs a new AjaxController instance.
* *
* @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
* The controller resolver. * The controller resolver.
* @param \Drupal\Core\Ajax\AjaxResponseRenderer $ajax_renderer
* The Ajax response renderer.
*/ */
public function __construct(ControllerResolverInterface $controller_resolver) { public function __construct(ControllerResolverInterface $controller_resolver, AjaxResponseRenderer $ajax_renderer) {
$this->controllerResolver = $controller_resolver; $this->controllerResolver = $controller_resolver;
$this->ajaxRenderer = $ajax_renderer;
} }
/** /**
* Controller method for AJAX content. * Controller method for Ajax content.
* *
* @param \Symfony\Component\HttpFoundation\Request $request * @param \Symfony\Component\HttpFoundation\Request $request
* The request object. * The request object.
* @param callable $_content * @param callable $_content
* The callable that returns the content of the ajax response. * The callable that returns the content of the Ajax response.
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Drupal\Core\Ajax\AjaxResponse
* A response object. * A response object.
*/ */
public function content(Request $request, $_content) { public function content(Request $request, $_content) {
$content = $this->getContentResult($request, $_content); $content = $this->getContentResult($request, $_content);
// If there is already an AjaxResponse, then return it without return $this->ajaxRenderer->render($content);
// manipulation.
if ($content instanceof AjaxResponse && $content->isOk()) {
return $content;
}
// Allow controllers to return a HtmlFragment or a Response object directly.
if ($content instanceof HtmlFragment) {
$content = $content->getContent();
}
if ($content instanceof Response) {
$content = $content->getContent();
}
// Most controllers return a render array, but some return a string.
if (!is_array($content)) {
$content = array(
'#markup' => $content,
);
}
$html = drupal_render($content);
$response = new AjaxResponse();
// The selector for the insert command is NULL as the new content will
// replace the element making the ajax call. The default 'replaceWith'
// behavior can be changed with #ajax['method'].
$response->addCommand(new InsertCommand(NULL, $html));
$status_messages = array('#theme' => 'status_messages');
$output = drupal_render($status_messages);
if (!empty($output)) {
$response->addCommand(new PrependCommand(NULL, $output));
}
return $response;
} }
/** /**
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\Core\EventSubscriber; namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Ajax\AjaxResponseRenderer;
use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Page\HtmlPage; use Drupal\Core\Page\HtmlPage;
use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface;
...@@ -42,6 +43,13 @@ class ViewSubscriber implements EventSubscriberInterface { ...@@ -42,6 +43,13 @@ class ViewSubscriber implements EventSubscriberInterface {
*/ */
protected $titleResolver; protected $titleResolver;
/**
* The Ajax response renderer.
*
* @var \Drupal\Core\Ajax\AjaxResponseRenderer
*/
protected $ajaxRenderer;
/** /**
* Constructs a new ViewSubscriber. * Constructs a new ViewSubscriber.
* *
...@@ -49,10 +57,13 @@ class ViewSubscriber implements EventSubscriberInterface { ...@@ -49,10 +57,13 @@ class ViewSubscriber implements EventSubscriberInterface {
* The content negotiation. * The content negotiation.
* @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
* The title resolver. * The title resolver.
* @param \Drupal\Core\Ajax\AjaxResponseRenderer $ajax_renderer
* The ajax response renderer.
*/ */
public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver) { public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver, AjaxResponseRenderer $ajax_renderer) {
$this->negotiation = $negotiation; $this->negotiation = $negotiation;
$this->titleResolver = $title_resolver; $this->titleResolver = $title_resolver;
$this->ajaxRenderer = $ajax_renderer;
} }
/** /**
...@@ -118,26 +129,8 @@ public function onJson(GetResponseForControllerResultEvent $event) { ...@@ -118,26 +129,8 @@ public function onJson(GetResponseForControllerResultEvent $event) {
return $response; return $response;
} }
public function onAjax(GetResponseForControllerResultEvent $event) {
$page_callback_result = $event->getControllerResult();
// Construct the response content from the page callback result.
$commands = ajax_prepare_response($page_callback_result);
$json = ajax_render($commands);
// Build the actual response object.
$response = new JsonResponse();
$response->setContent($json);
return $response;
}
public function onIframeUpload(GetResponseForControllerResultEvent $event) { public function onIframeUpload(GetResponseForControllerResultEvent $event) {
$page_callback_result = $event->getControllerResult(); $response = $event->getResponse();
// Construct the response content from the page callback result.
$commands = ajax_prepare_response($page_callback_result);
$json = ajax_render($commands);
// Browser IFRAMEs expect HTML. Browser extensions, such as Linkification // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
// and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
...@@ -145,7 +138,7 @@ public function onIframeUpload(GetResponseForControllerResultEvent $event) { ...@@ -145,7 +138,7 @@ public function onIframeUpload(GetResponseForControllerResultEvent $event) {
// JSON data by making it the value of a textarea. // JSON data by making it the value of a textarea.
// @see http://malsup.com/jquery/form/#file-upload // @see http://malsup.com/jquery/form/#file-upload
// @see http://drupal.org/node/1009382 // @see http://drupal.org/node/1009382
$html = '<textarea>' . $json . '</textarea>'; $html = '<textarea>' . $response->getContent() . '</textarea>';
return new Response($html); return new Response($html);
} }
......
...@@ -37,7 +37,10 @@ public function __construct(ContentNegotiation $negotiation) { ...@@ -37,7 +37,10 @@ public function __construct(ContentNegotiation $negotiation) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function enhance(array $defaults, Request $request) { public function enhance(array $defaults, Request $request) {
if (empty($defaults['_content']) && $defaults['_controller'] != 'controller.ajax:content' && $this->negotiation->getContentType($request) == 'drupal_ajax') { // A request can have the 'ajax' content type when the controller supports
// basically both simple HTML and Ajax routes by returning a render array.
// In those cases we want to convert it to a proper ajax response as well.
if (empty($defaults['_content']) && $defaults['_controller'] != 'controller.ajax:content' && in_array($this->negotiation->getContentType($request), array('drupal_ajax', 'ajax', 'iframeupload'))) {
$defaults['_content'] = isset($defaults['_controller']) ? $defaults['_controller'] : NULL; $defaults['_content'] = isset($defaults['_controller']) ? $defaults['_controller'] : NULL;
$defaults['_controller'] = 'controller.ajax:content'; $defaults['_controller'] = 'controller.ajax:content';
} }
......
...@@ -1667,7 +1667,8 @@ protected function drupalProcessAjaxResponse($content, array $ajax_response, arr ...@@ -1667,7 +1667,8 @@ protected function drupalProcessAjaxResponse($content, array $ajax_response, arr
} }
// @todo Ajax commands can target any jQuery selector, but these are // @todo Ajax commands can target any jQuery selector, but these are
// hard to fully emulate with XPath. For now, just handle 'head' // hard to fully emulate with XPath. For now, just handle 'head'
// and 'body', since these are used by ajax_render(). // and 'body', since these are used by
// \Drupal\Core\Ajax\AjaxResponse::ajaxRender().
elseif (in_array($command['selector'], array('head', 'body'))) { elseif (in_array($command['selector'], array('head', 'body'))) {
$wrapperNode = $xpath->query('//' . $command['selector'])->item(0); $wrapperNode = $xpath->query('//' . $command['selector'])->item(0);
} }
......
...@@ -24,22 +24,22 @@ abstract class AjaxTestBase extends WebTestBase { ...@@ -24,22 +24,22 @@ abstract class AjaxTestBase extends WebTestBase {
/** /**
* Asserts the array of Ajax commands contains the searched command. * Asserts the array of Ajax commands contains the searched command.
* *
* The Ajax framework, via the ajax_render() function, returns an array of * An AjaxResponse object stores an array of Ajax commands. This array
* commands. This array sometimes includes commands automatically provided by * sometimes includes commands automatically provided by the framework in
* the framework in addition to commands returned by a particular page * addition to commands returned by a particular controller. During testing,
* callback. During testing, we're usually interested that a particular * we're usually interested that a particular command is present, and don't
* command is present, and don't care whether other commands precede or * care whether other commands precede or follow the one we're interested in.
* follow the one we're interested in. Additionally, the command we're * Additionally, the command we're interested in may include additional data
* interested in may include additional data that we're not interested in. * that we're not interested in. Therefore, this function simply asserts that
* Therefore, this function simply asserts that one of the commands in * one of the commands in $haystack contains all of the keys and values in
* $haystack contains all of the keys and values in $needle. Furthermore, if * $needle. Furthermore, if $needle contains a 'settings' key with an array
* $needle contains a 'settings' key with an array value, we simply assert * value, we simply assert that all keys and values within that array are
* that all keys and values within that array are present in the command we're * present in the command we're checking, and do not consider it a failure if
* checking, and do not consider it a failure if the actual command contains * the actual command contains additional settings that aren't part of
* additional settings that aren't part of $needle. * $needle.
* *
* @param $haystack * @param $haystack
* An array of Ajax commands returned by the server. * An array of rendered Ajax commands returned by the server.
* @param $needle * @param $needle
* Array of info we're expecting in one of those commands. * Array of info we're expecting in one of those commands.
* @param $message * @param $message
......
...@@ -27,14 +27,14 @@ public static function getInfo() { ...@@ -27,14 +27,14 @@ public static function getInfo() {
} }
/** /**
* Ensures ajax_render() returns JavaScript settings from the page request. * Ensures \Drupal\Core\Ajax\AjaxResponse::ajaxRender() returns JavaScript settings from the page request.
*/ */
public function testAJAXRender() { public function testAJAXRender() {
// Verify that settings command is generated when JavaScript settings are // Verify that settings command is generated when JavaScript settings are
// set via _drupal_add_js(). // set via _drupal_add_js().
$commands = $this->drupalGetAJAX('ajax-test/render'); $commands = $this->drupalGetAJAX('ajax-test/render');
$expected = new SettingsCommand(array('ajax' => 'test'), TRUE); $expected = new SettingsCommand(array('ajax' => 'test'), TRUE);
$this->assertCommand($commands, $expected->render(), 'ajax_render() loads settings added with _drupal_add_js().'); $this->assertCommand($commands, $expected->render(), '\Drupal\Core\Ajax\AjaxResponse::ajaxRender() loads settings added with _drupal_add_js().');
} }
/** /**
...@@ -101,7 +101,7 @@ public function testOrder() { ...@@ -101,7 +101,7 @@ public function testOrder() {
} }
/** /**
* Tests behavior of ajax_render_error(). * Tests the behavior of an error alert command.
*/ */
public function testAJAXRenderError() { public function testAJAXRenderError() {
// Verify custom error message. // Verify custom error message.
......
...@@ -399,17 +399,18 @@ function hook_css_alter(&$css) { ...@@ -399,17 +399,18 @@ function hook_css_alter(&$css) {
} }
/** /**
* Alter the commands that are sent to the user through the Ajax framework. * Alter the Ajax command data that is sent to the client.
* *
* @param $commands * @param \Drupal\Core\Ajax\CommandInterface[] $data
* An array of all commands that will be sent to the user. * An array of all the rendered commands that will be sent to the client.
* *
* @see ajax_render() * @see \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
*/ */
function hook_ajax_render_alter($commands) { function hook_ajax_render_alter(array &$data) {
// Inject any new status messages into the content area. // Inject any new status messages into the content area.
$status_messages = array('#theme' => 'status_messages'); $status_messages = array('#theme' => 'status_messages');
$commands[] = ajax_command_prepend('#block-system-main .content', drupal_render($status_messages)); $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', drupal_render($status_messages));
$data[] = $command->render();
} }
/** /**
......
...@@ -21,11 +21,12 @@ function ajax_test_system_theme_info() { ...@@ -21,11 +21,12 @@ function ajax_test_system_theme_info() {
} }
/** /**
* Menu callback: Returns an element suitable for use by ajax_render(). * Menu callback: Returns an element suitable for use by
* \Drupal\Core\Ajax\AjaxResponse::ajaxRender().
* *
* Additionally ensures that ajax_render() incorporates JavaScript settings * Additionally ensures that \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
* generated during the page request by invoking _drupal_add_js() with a dummy * incorporates JavaScript settings generated during the page request by
* setting. * invoking _drupal_add_js() with a dummy setting.
* *
* @deprecated \Drupal\ajax_test\Controller\AjaxTestController::render() * @deprecated \Drupal\ajax_test\Controller\AjaxTestController::render()
*/ */
......
...@@ -123,8 +123,9 @@ public function getForm(ViewStorageInterface $view, $display_id, $js) { ...@@ -123,8 +123,9 @@ public function getForm(ViewStorageInterface $view, $display_id, $js) {
// With the below logic, we may end up rendering a form twice (or two forms // With the below logic, we may end up rendering a form twice (or two forms
// each sharing the same element ids), potentially resulting in // each sharing the same element ids), potentially resulting in
// _drupal_add_js() being called twice to add the same setting. drupal_get_js() // _drupal_add_js() being called twice to add the same setting. drupal_get_js()
// is ok with that, but until ajax_render() is (http://drupal.org/node/208611), // is ok with that, but until \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
// reset the _drupal_add_js() static before rendering the second time. // is (http://drupal.org/node/208611), reset the _drupal_add_js() static
// before rendering the second time.
$drupal_add_js_original = _drupal_add_js(); $drupal_add_js_original = _drupal_add_js();
$drupal_add_js = &drupal_static('_drupal_add_js'); $drupal_add_js = &drupal_static('_drupal_add_js');
$response = views_ajax_form_wrapper($form_state['form_id'], $form_state); $response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment