Commit 6c8f58a3 authored by catch's avatar catch

Issue #2597359 by Wim Leers: Require responses with attachments to contain the...

Issue #2597359 by Wim Leers: Require responses with attachments to contain the final attachment values
parent 828b769e
......@@ -148,6 +148,16 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req
$css_assets = $this->assetResolver->getCssAssets($assets, $optimize_css);
list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js);
// First, AttachedAssets::setLibraries() ensures duplicate libraries are
// removed: it converts it to a set of libraries if necessary. Second,
// AssetResolver::getJsSettings() ensures $assets contains the final set of
// JavaScript settings. AttachmentsResponseProcessorInterface also mandates
// that the response it processes contains the final attachment values, so
// update both the 'library' and 'drupalSettings' attachments accordingly.
$attachments['library'] = $assets->getLibraries();
$attachments['drupalSettings'] = $assets->getSettings();
$response->setAttachments($attachments);
// Render the HTML to load these files, and add AJAX commands to insert this
// HTML in the page. Settings are handled separately, afterwards.
$settings = [];
......
......@@ -334,6 +334,9 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
// Allow modules and themes to alter the JavaScript settings.
$this->moduleHandler->alter('js_settings', $settings, $assets);
$this->themeManager->alter('js_settings', $settings, $assets);
// Update the $assets object accordingly, so that it reflects the final
// settings.
$assets->setSettings($settings);
$settings_as_inline_javascript = [
'type' => 'setting',
'group' => JS_SETTING,
......
......@@ -69,6 +69,8 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize);
*
* @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
* The assets attached to the current response.
* Note that this object is modified to reflect the final JavaScript
* settings assets.
* @param bool $optimize
* Whether to apply the JavaScript asset collection optimizer, to return
* optimized JavaScript asset collections rather than an unoptimized ones.
......
......@@ -46,7 +46,8 @@ interface AttachmentsResponseProcessorInterface {
* The response to process.
*
* @return \Drupal\Core\Render\AttachmentsInterface
* The processed response.
* The processed response, with the attachments updated to reflect their
* final values.
*
* @throws \InvalidArgumentException
* Thrown when the $response parameter is not the type of response object
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Asset\AssetCollectionRendererInterface;
use Drupal\Core\Asset\AssetResolverInterface;
use Drupal\Core\Asset\AttachedAssets;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\EnforcedResponseException;
use Drupal\Core\Extension\ModuleHandlerInterface;
......@@ -155,7 +156,19 @@ public function processAttachments(AttachmentsInterface $response) {
$attachment_placeholders = $attached['html_response_attachment_placeholders'];
unset($attached['html_response_attachment_placeholders']);
$variables = $this->processAssetLibraries($attached, $attachment_placeholders);
$assets = AttachedAssets::createFromRenderArray(['#attached' => $attached]);
// Take Ajax page state into account, to allow for something like
// Turbolinks to be implemented without altering core.
// @see https://github.com/rails/turbolinks/
$ajax_page_state = $this->requestStack->getCurrentRequest()->get('ajax_page_state');
$assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
$variables = $this->processAssetLibraries($assets, $attachment_placeholders);
// $variables now contains the markup to load the asset libraries. Update
// $attached with the final list of libraries and JavaScript settings, so
// that $response can be updated with those. Then the response object will
// list the final, processed attachments.
$attached['library'] = $assets->getLibraries();
$attached['drupalSettings'] = $assets->getSettings();
// Since we can only replace content in the HTML head section if there's a
// placeholder for it, we can safely avoid processing the render array if
......@@ -168,6 +181,7 @@ public function processAttachments(AttachmentsInterface $response) {
$attached,
$this->processFeed($attached['feed'])
);
unset($attached['feed']);
}
// 'html_head_link' is a special case of 'html_head' which can be present
// as a head element, but also as a Link: HTTP header depending on
......@@ -182,6 +196,7 @@ public function processAttachments(AttachmentsInterface $response) {
$attached,
$this->processHtmlHeadLink($attached['html_head_link'])
);
unset($attached['html_head_link']);
}
// Now we can process 'html_head', which contains both 'feed' and
......@@ -200,6 +215,10 @@ public function processAttachments(AttachmentsInterface $response) {
$this->setHeaders($response, $attached['http_header']);
}
// AttachmentsResponseProcessorInterface mandates that the response it
// processes contains the final attachment values.
$response->setAttachments($attached);
return $response;
}
......@@ -255,8 +274,8 @@ protected function renderPlaceholders(HtmlResponse $response) {
/**
* Processes asset libraries into render arrays.
*
* @param array $attached
* The attachments to process.
* @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
* The attached assets collection for the current response.
* @param array $placeholders
* The placeholders that exist in the response.
*
......@@ -266,16 +285,7 @@ protected function renderPlaceholders(HtmlResponse $response) {
* - scripts
* - scripts_bottom
*/
protected function processAssetLibraries(array $attached, array $placeholders) {
$all_attached = ['#attached' => $attached];
$assets = AttachedAssets::createFromRenderArray($all_attached);
// Take Ajax page state into account, to allow for something like Turbolinks
// to be implemented without altering core.
// @see https://github.com/rails/turbolinks/
$ajax_page_state = $this->requestStack->getCurrentRequest()->get('ajax_page_state');
$assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
protected function processAssetLibraries(AttachedAssetsInterface $assets, array $placeholders) {
$variables = [];
// Print styles - if present.
......
......@@ -108,8 +108,10 @@ function testAddJsSettings() {
$build['#attached']['library'][] = 'core/drupalSettings';
$assets = AttachedAssets::createFromRenderArray($build);
$this->assertEqual([], $assets->getSettings(), 'JavaScript settings on $assets are empty.');
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.');
$this->assertTrue(array_key_exists('currentPath', $assets->getSettings()['path']), 'JavaScript settings on $assets are resolved after retrieving JavaScript assets, and are equal to the returned JavaScript settings.');
$assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]);
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
......
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