diff --git a/core/lib/Drupal/Core/Ajax/AddCssCommand.php b/core/lib/Drupal/Core/Ajax/AddCssCommand.php new file mode 100644 index 0000000..cfb693f --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/AddCssCommand.php @@ -0,0 +1,42 @@ +styles = $styles; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'add_css', + 'data' => $this->styles, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/AfterCommand.php b/core/lib/Drupal/Core/Ajax/AfterCommand.php new file mode 100644 index 0000000..691ce95 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/AfterCommand.php @@ -0,0 +1,38 @@ + 'insert', + 'method' => 'append', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponse.php b/core/lib/Drupal/Core/Ajax/AjaxResponse.php new file mode 100644 index 0000000..fb10e2a --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/AjaxResponse.php @@ -0,0 +1,107 @@ +commands[] = $command->render(); + } + + /** + * Sets the response's data to be the array of AJAX commands. + * + * @param $request A request object. + */ + public function prepare(Request $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($_POST['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], $_POST['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); + + $extra_commands = array(); + 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); + + parent::setData($commands); + parent::prepare($request); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/AlertCommand.php b/core/lib/Drupal/Core/Ajax/AlertCommand.php new file mode 100644 index 0000000..5842934 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/AlertCommand.php @@ -0,0 +1,37 @@ +text = $text; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'alert', + 'text' => $this->text, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/AppendCommand.php b/core/lib/Drupal/Core/Ajax/AppendCommand.php new file mode 100644 index 0000000..af429ea --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/AppendCommand.php @@ -0,0 +1,34 @@ + 'insert', + 'method' => 'append', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/BeforeCommand.php b/core/lib/Drupal/Core/Ajax/BeforeCommand.php new file mode 100644 index 0000000..caf7d43 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/BeforeCommand.php @@ -0,0 +1,37 @@ + 'insert', + 'method' => 'before', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/ChangedCommand.php b/core/lib/Drupal/Core/Ajax/ChangedCommand.php new file mode 100644 index 0000000..c5b87d9 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/ChangedCommand.php @@ -0,0 +1,50 @@ +selector = $selector; + $this->asterisk = $asterisk; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'changed', + 'selector' => $this->selector, + 'asterisk' => $this->asterisk, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/CommandInterface.php b/core/lib/Drupal/Core/Ajax/CommandInterface.php new file mode 100644 index 0000000..2806750 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/CommandInterface.php @@ -0,0 +1,21 @@ +selector = $selector; + $this->css = $css; + } + + /** + * Adds a key/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, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/DataCommand.php b/core/lib/Drupal/Core/Ajax/DataCommand.php new file mode 100644 index 0000000..09b69d7 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/DataCommand.php @@ -0,0 +1,59 @@ +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, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/HtmlCommand.php b/core/lib/Drupal/Core/Ajax/HtmlCommand.php new file mode 100644 index 0000000..bfeb56e --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/HtmlCommand.php @@ -0,0 +1,36 @@ + 'insert', + 'method' => 'html', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/InsertCommand.php b/core/lib/Drupal/Core/Ajax/InsertCommand.php new file mode 100644 index 0000000..4c58aec --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/InsertCommand.php @@ -0,0 +1,59 @@ +selector = $selector; + $this->html = $html; + $this->settings = $settings; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'insert', + 'method' => NULL, + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/InvokeCommand.php b/core/lib/Drupal/Core/Ajax/InvokeCommand.php new file mode 100644 index 0000000..f41d5d4 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/InvokeCommand.php @@ -0,0 +1,56 @@ +selector = $selector; + $this->method = $method; + $this->arguments = $arguments; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'invoke', + 'selector' => $this->selector, + 'method' => $this->method, + 'args' => $this->arguments, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/PrependCommand.php b/core/lib/Drupal/Core/Ajax/PrependCommand.php new file mode 100644 index 0000000..6cecbd2 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/PrependCommand.php @@ -0,0 +1,40 @@ + 'insert', + 'method' => 'prepend', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/RemoveCommand.php b/core/lib/Drupal/Core/Ajax/RemoveCommand.php new file mode 100644 index 0000000..03678d1 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/RemoveCommand.php @@ -0,0 +1,41 @@ +selecor = $selector; + } + + public function render() { + return array( + 'command' => 'remove', + 'selector' => $this->selector, + ); + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/ReplaceCommand.php b/core/lib/Drupal/Core/Ajax/ReplaceCommand.php new file mode 100644 index 0000000..7d8a8a9 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/ReplaceCommand.php @@ -0,0 +1,38 @@ + 'insert', + 'method' => 'replaceWith', + 'selector' => $this->selector, + 'data' => $this->html, + 'settings' => $this->settings, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/RestripeCommand.php b/core/lib/Drupal/Core/Ajax/RestripeCommand.php new file mode 100644 index 0000000..76f60ae --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/RestripeCommand.php @@ -0,0 +1,42 @@ +selector = $selector; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'restripe', + 'selector' => $this->selector, + ); + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Ajax/SettingsCommand.php b/core/lib/Drupal/Core/Ajax/SettingsCommand.php new file mode 100644 index 0000000..26ee465 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/SettingsCommand.php @@ -0,0 +1,55 @@ +settings = $settings; + $this->merge = $merge; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return array( + 'command' => 'settings', + 'settings' => $this->argument, + 'merge' => $this->merge, + ); + } + +} \ No newline at end of file diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 4e6c8eb..e3d768c 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Drupal\file\FileUsage\DatabaseFileUsageBackend; use Drupal\file\FileUsage\FileUsageInterface; +use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\Ajax\AjaxCommandReplace; // Load all Field module hooks for File. require_once DRUPAL_ROOT . '/core/modules/file/file.field.inc'; @@ -797,9 +799,9 @@ function file_ajax_upload() { if (empty($_POST['form_build_id']) || $form_build_id != $_POST['form_build_id']) { // Invalid request. drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error'); - $commands = array(); - $commands[] = ajax_command_replace(NULL, theme('status_messages')); - return array('#type' => 'ajax', '#commands' => $commands); + $response = new AjaxResponse(); + $response->addCommand(new AjaxCommandReplace(NULL, theme('status_messages'))); + return $response; } list($form, $form_state) = ajax_get_form(); @@ -807,9 +809,9 @@ function file_ajax_upload() { if (!$form) { // Invalid form_build_id. drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error'); - $commands = array(); - $commands[] = ajax_command_replace(NULL, theme('status_messages')); - return array('#type' => 'ajax', '#commands' => $commands); + $response = new AjaxResponse(); + $response->addCommand(new AjaxCommandReplace(NULL, theme('status_messages'))); + return $response; } // Get the current element and count the number of files. @@ -835,16 +837,16 @@ function file_ajax_upload() { else { $form['#suffix'] .= ''; } - $output = theme('status_messages') . drupal_render($form); $js = drupal_add_js(); $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']); - $commands = array(); - $commands[] = ajax_command_replace(NULL, $output, $settings); - return array('#type' => 'ajax', '#commands' => $commands); + $response = new AjaxResponse(); + $response->addCommand(new AjaxCommandReplace(NULL, $output, $settings)); + return $response; } + /** * Ajax callback: Retrieves upload progress. *