Commit 95feb071 authored by Dries's avatar Dries

Issue #1812866 by mkadin, effulgentsia: rebuild the server side AJAX API.

parent 1916c77a
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\AddCssCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* An AJAX command for adding css to the page via ajax.
*
* This command is implemented by Drupal.ajax.prototype.commands.add_css()
* defined in misc/ajax.js.
*
* @see misc/ajax.js
*/
class AddCssCommand implements CommandInterface {
/**
* A string that contains the styles to be added to the page.
*
* It should include the wrapping style tag.
*
* @var string
*/
protected $styles;
/**
* Constructs an AddCssCommand.
*
* @param string $styles
* A string that contains the styles to be added to the page, including the
* wrapping <style> tag.
*/
public function __construct($styles) {
$this->styles = $styles;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'add_css',
'data' => $this->styles,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\AfterCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\InsertCommand;
/**
* An AJAX command for calling the jQuery after() method.
*
* The 'insert/after' command instructs the client to use jQuery's after()
* method to insert the given HTML content after each element matched by the
* given selector.
*
* This command is implemented by Drupal.ajax.prototype.commands.insert()
* defined in misc/ajax.js.
*
* @see http://docs.jquery.com/Manipulation/after#content
*/
class AfterCommand extends InsertCommand {
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'insert',
'method' => 'after',
'selector' => $this->selector,
'data' => $this->html,
'settings' => $this->settings,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\AjaxResponse.
*/
namespace Drupal\Core\Ajax;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* JSON response object for AJAX requests.
*/
class AjaxResponse extends JsonResponse {
/**
* The array of ajax commands.
*
* @var array
*/
protected $commands = array();
/**
* Add an AJAX command to the response.
*
* @param object $command
* An AJAX command object implementing CommandInterface.
*
* @return AjaxResponse
* The current AjaxResponse.
*/
public function addCommand($command) {
$this->commands[] = $command->render();
return $this;
}
/**
* Sets the response's data to be the array of AJAX commands.
*
* @param
* $request A request object.
*
* @return
* Response The current response.
*/
public function prepare(Request $request) {
parent::setData($this->ajaxRender($request));
return parent::prepare($request);
}
/**
* Prepares the AJAX commands for sending back to the client.
*
* @param Request
* The request object that the AJAX is responding to.
*
* @return array
* An array of commands ready to be returned as JSON.
*/
protected function ajaxRender($request) {
// Ajax responses aren't rendered with html.tpl.php, so we have to call
// drupal_get_css() and drupal_get_js() here, in order to have new files
// added during this request to be loaded by the page. We only want to send
// back files that the page hasn't already loaded, so we implement simple
// diffing logic using array_diff_key().
foreach (array('css', 'js') as $type) {
// It is highly suspicious if $_POST['ajax_page_state'][$type] is empty,
// since the base page ought to have at least one JS file and one CSS file
// loaded. It probably indicates an error, and rather than making the page
// reload all of the files, instead we return no new files.
if (empty($request->parameters['ajax_page_state'][$type])) {
$items[$type] = array();
}
else {
$function = 'drupal_add_' . $type;
$items[$type] = $function();
drupal_alter($type, $items[$type]);
// @todo Inline CSS and JS items are indexed numerically. These can't be
// reliably diffed with array_diff_key(), since the number can change
// due to factors unrelated to the inline content, so for now, we
// strip the inline items from Ajax responses, and can add support for
// them when drupal_add_css() and drupal_add_js() are changed to using
// md5() or some other hash of the inline content.
foreach ($items[$type] as $key => $item) {
if (is_numeric($key)) {
unset($items[$type][$key]);
}
}
// Ensure that the page doesn't reload what it already has.
$items[$type] = array_diff_key($items[$type], $request->parameters['ajax_page_state'][$type]);
}
}
// Render the HTML to load these files, and add AJAX commands to insert this
// HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
// data from being altered again, as we already altered it above. Settings
// are handled separately, afterwards.
if (isset($items['js']['settings'])) {
unset($items['js']['settings']);
}
$styles = drupal_get_css($items['css'], TRUE);
$scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
$scripts_header = drupal_get_js('header', $items['js'], TRUE);
if (!empty($styles)) {
$this->addCommand(new AddCssCommand($styles));
}
if (!empty($scripts_header)) {
$this->addCommand(new PrependCommand('head', $scripts_header));
}
if (!empty($scripts_footer)) {
$this->addCommand(new AppendCommand('body', $scripts_footer));
}
// Now add a command to merge changes and additions to Drupal.settings.
$scripts = drupal_add_js();
if (!empty($scripts['settings'])) {
$settings = $scripts['settings'];
$this->addCommand(new SettingsCommand(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
}
$commands = $this->commands;
drupal_alter('ajax_render', $commands);
return $commands;
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\AlertCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* AJAX command for a javascript alert box.
*/
class AlertCommand implements CommandInterface {
/**
* The text to be displayed in the alert box.
*
* @var string
*/
protected $text;
/**
* Constructs an AlertCommand object.
*
* @param string $text
* The text to be displayed in the alert box.
*/
public function __construct($text) {
$this->text = $text;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'alert',
'text' => $this->text,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\AppendCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\InsertCommand;
/**
* An AJAX command for calling the jQuery append() method.
*
* The 'insert/append' command instructs the client to use jQuery's append()
* method to append the given HTML content to the inside of each element matched
* by the given selector.
*
* This command is implemented by Drupal.ajax.prototype.commands.insert()
* defined in misc/ajax.js.
*
* @see http://docs.jquery.com/Manipulation/append#content
*/
class AppendCommand extends InsertCommand {
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'insert',
'method' => 'append',
'selector' => $this->selector,
'data' => $this->html,
'settings' => $this->settings,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\InsertCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\InsertCommand;
/**
* An AJAX command for calling the jQuery before() method.
*
* The 'insert/before' command instructs the client to use jQuery's before()
* method to insert the given HTML content before each of elements matched by
* the given selector.
*
* This command is implemented by Drupal.ajax.prototype.commands.insert()
* defined in misc/ajax.js.
*
* @see http://docs.jquery.com/Manipulation/before#content
*/
class BeforeCommand extends InsertCommand {
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'insert',
'method' => 'before',
'selector' => $this->selector,
'data' => $this->html,
'settings' => $this->settings,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\ChangedCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* An AJAX command for marking HTML elements as changed.
*
* This command instructs the client to mark each of the elements matched by the
* given selector as 'ajax-changed'.
*
* This command is implemented by Drupal.ajax.prototype.commands.changed()
* defined in misc/ajax.js.
*/
class ChangedCommand implements CommandInterface {
/**
* A CSS selector string.
*
* If the command is a response to a request from an #ajax form element then
* this value can be NULL.
*
* @var string
*/
protected $selector;
/**
* An optional CSS selector for elements to which asterisks will be appended.
*
* @var string
*/
protected $asterisk;
/**
* Constructs a ChangedCommand object.
*
* @param string $selector
* CSS selector for elements to be marked as changed.
* @param string $asterisk
* CSS selector for elements to which an asterisk will be appended.
*/
public function __construct($selector, $asterisk = '') {
$this->selector = $selector;
$this->asterisk = $asterisk;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'changed',
'selector' => $this->selector,
'asterisk' => $this->asterisk,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\CommandInterface.
*/
namespace Drupal\Core\Ajax;
/**
* AJAX command interface.
*
* All AJAX commands passed to AjaxResponse objects should implement these
* methods.
*/
interface CommandInterface {
/**
* Return an array to be run through json_encode and sent to the client.
*/
public function render();
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\CssCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* An AJAX command for calling the jQuery css() method.
*
* The 'css' command will instruct the client to use the jQuery css() method to
* apply the CSS arguments to elements matched by the given selector.
*
* This command is implemented by Drupal.ajax.prototype.commands.css() defined
* in misc/ajax.js.
*
* @see http://docs.jquery.com/CSS/css#properties
*/
class CssCommand implements CommandInterface {
/**
* A CSS selector string.
*
* If the command is a response to a request from an #ajax form element then
* this value can be NULL.
*
* @var string
*/
protected $selector;
/**
* An array of property/value pairs to set in the CSS for the selector.
*
* @var array
*/
protected $css = array();
/**
* Constructs a CssCommand object.
*
* @param string $selector
* A CSS selector for elements to which the CSS will be applied.
* @param array $css
* An array of CSS property/value pairs to set.
*/
public function __construct($selector, array $css = array()) {
$this->selector = $selector;
$this->css = $css;
}
/**
* Adds a property/value pair to the CSS to be added to this element.
*
* @param $property
* The CSS property to be changed.
* @param $value
* The new value of the CSS property.
*/
public function setProperty($property, $value) {
$this->css[$property] = $value;
return $this;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'css',
'selector' => $this->selector,
'argument' => $this->css,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\DataCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* An AJAX command for implementing jQuery's data() method.
*
* This instructs the client to attach the name=value pair of data to the
* selector via jQuery's data cache.
*
* This command is implemented by Drupal.ajax.prototype.commands.data() defined
* in misc/ajax.js.
*/
class DataCommand implements CommandInterface {
/**
* A CSS selector string for elements to which data will be attached.
*
* If the command is a response to a request from an #ajax form element then
* this value can be NULL.
*
* @var string
*/
protected $selector;
/**
* The key of the data attached to elements matched by the selector.
*
* @var string
*/
protected $name;
/**
* The value of the data to be atached to elements matched by the selector.
*
* The data is not limited to strings; it can be any format.
*
* @var mixed
*/
protected $value;
/**
* Constructs a DataCommand object.
*
* @param string $selector
* A CSS selector for the elements to which the data will be attached.
* @param string $name
* The key of the data to be attached to elements matched by the selector.
* @param type $value
* The value of the data to be attached to elements matched by the selector.
*/
public function __construct($selector, $name, $value) {
$this->selector = $selector;
$this->name = $name;
$this->value = $value;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'data',
'selector' => $this->selector,
'name' => $this->name,
'value' => $this->value,
);
}
}
<?php
/**
* @file
* Definition of Drupal\Core\Ajax\HtmlCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\InsertCommand;
/**
* AJAX command for calling the jQuery html() method.
*
* The 'insert/html' command instructs the client to use jQuery's html() method