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:
arguments: ['@controller_resolver', '@string_translation', '@title_resolver']
controller.ajax:
class: Drupal\Core\Controller\AjaxController
arguments: ['@controller_resolver']
arguments: ['@controller_resolver', '@ajax_response_renderer']
controller.entityform:
class: Drupal\Core\Entity\HtmlEntityFormController
arguments: ['@controller_resolver', '@service_container', '@entity.manager']
controller.dialog:
class: Drupal\Core\Controller\DialogController
arguments: ['@controller_resolver', '@title_resolver']
ajax_response_renderer:
class: Drupal\Core\Ajax\AjaxResponseRenderer
router_listener:
class: Symfony\Component\HttpKernel\EventListener\RouterListener
tags:
......@@ -428,7 +430,7 @@ services:
class: Drupal\Core\EventSubscriber\ViewSubscriber
tags:
- { name: event_subscriber }
arguments: ['@content_negotiation', '@title_resolver']
arguments: ['@content_negotiation', '@title_resolver', '@ajax_response_renderer']
html_view_subscriber:
class: Drupal\Core\EventSubscriber\HtmlViewSubscriber
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 @@
namespace Drupal\Core\Controller;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InsertCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Page\HtmlFragment;
use Drupal\Core\Page\HtmlPage;
use Drupal\Core\Ajax\AjaxResponseRenderer;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Default controller for ajax requests.
* Default controller for Ajax requests.
*/
class AjaxController extends ContainerAware {
......@@ -28,63 +23,40 @@ class AjaxController extends ContainerAware {
*/
protected $controllerResolver;
/**
* The Ajax response renderer.
*
* @var \Drupal\Core\Ajax\AjaxResponseRenderer
*/
protected $ajaxRenderer;
/**
* Constructs a new AjaxController instance.
*
* @param \Drupal\Core\Controller\ControllerResolverInterface $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->ajaxRenderer = $ajax_renderer;
}
/**
* Controller method for AJAX content.
* Controller method for Ajax content.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @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.
*/
public function content(Request $request, $_content) {
$content = $this->getContentResult($request, $_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();
}
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;
return $this->ajaxRenderer->render($content);
}
/**
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Ajax\AjaxResponseRenderer;
use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Page\HtmlPage;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
......@@ -42,6 +43,13 @@ class ViewSubscriber implements EventSubscriberInterface {
*/
protected $titleResolver;
/**
* The Ajax response renderer.
*
* @var \Drupal\Core\Ajax\AjaxResponseRenderer
*/
protected $ajaxRenderer;
/**
* Constructs a new ViewSubscriber.
*
......@@ -49,10 +57,13 @@ class ViewSubscriber implements EventSubscriberInterface {
* The content negotiation.
* @param \Drupal\Core\Controller\TitleResolverInterface $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->titleResolver = $title_resolver;
$this->ajaxRenderer = $ajax_renderer;
}
/**
......@@ -118,26 +129,8 @@ public function onJson(GetResponseForControllerResultEvent $event) {
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) {
$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);
$response = $event->getResponse();
// Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
// and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
......@@ -145,7 +138,7 @@ public function onIframeUpload(GetResponseForControllerResultEvent $event) {
// JSON data by making it the value of a textarea.
// @see http://malsup.com/jquery/form/#file-upload
// @see http://drupal.org/node/1009382
$html = '<textarea>' . $json . '</textarea>';
$html = '<textarea>' . $response->getContent() . '</textarea>';
return new Response($html);
}
......
......@@ -37,7 +37,10 @@ public function __construct(ContentNegotiation $negotiation) {
* {@inheritdoc}
*/
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['_controller'] = 'controller.ajax:content';
}
......
......@@ -1667,7 +1667,8 @@ protected function drupalProcessAjaxResponse($content, array $ajax_response, arr
}
// @todo Ajax commands can target any jQuery selector, but these are
// 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'))) {
$wrapperNode = $xpath->query('//' . $command['selector'])->item(0);
}
......
......@@ -24,22 +24,22 @@ abstract class AjaxTestBase extends WebTestBase {
/**
* Asserts the array of Ajax commands contains the searched command.
*
* The Ajax framework, via the ajax_render() function, returns an array of
* commands. This array sometimes includes commands automatically provided by
* the framework in addition to commands returned by a particular page
* callback. During testing, we're usually interested that a particular
* command is present, and don't care whether other commands precede or
* follow the one we're interested in. Additionally, the command we're
* interested in may include additional data that we're not interested in.
* Therefore, this function simply asserts that one of the commands in
* $haystack contains all of the keys and values in $needle. Furthermore, if
* $needle contains a 'settings' key with an array value, we simply assert
* that all keys and values within that array are present in the command we're
* checking, and do not consider it a failure if the actual command contains
* additional settings that aren't part of $needle.
* An AjaxResponse object stores an array of Ajax commands. This array
* sometimes includes commands automatically provided by the framework in
* addition to commands returned by a particular controller. During testing,
* we're usually interested that a particular command is present, and don't
* care whether other commands precede or follow the one we're interested in.
* Additionally, the command we're interested in may include additional data
* that we're not interested in. Therefore, this function simply asserts that
* one of the commands in $haystack contains all of the keys and values in
* $needle. Furthermore, if $needle contains a 'settings' key with an array
* value, we simply assert that all keys and values within that array are
* present in the command we're checking, and do not consider it a failure if
* the actual command contains additional settings that aren't part of
* $needle.
*
* @param $haystack
* An array of Ajax commands returned by the server.
* An array of rendered Ajax commands returned by the server.
* @param $needle
* Array of info we're expecting in one of those commands.
* @param $message
......
......@@ -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() {
// Verify that settings command is generated when JavaScript settings are
// set via _drupal_add_js().
$commands = $this->drupalGetAJAX('ajax-test/render');
$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() {
}
/**
* Tests behavior of ajax_render_error().
* Tests the behavior of an error alert command.
*/
public function testAJAXRenderError() {
// Verify custom error message.
......
......@@ -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
* An array of all commands that will be sent to the user.
* @param \Drupal\Core\Ajax\CommandInterface[] $data
* 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.
$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() {
}
/**
* 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
* generated during the page request by invoking _drupal_add_js() with a dummy
* setting.
* Additionally ensures that \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
* incorporates JavaScript settings generated during the page request by
* invoking _drupal_add_js() with a dummy setting.
*
* @deprecated \Drupal\ajax_test\Controller\AjaxTestController::render()
*/
......
......@@ -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
// each sharing the same element ids), potentially resulting in
// _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),
// reset the _drupal_add_js() static before rendering the second time.
// is ok with that, but until \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
// 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 = &drupal_static('_drupal_add_js');
$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