core/core.services.yml | 1 - core/lib/Drupal/Core/Ajax/OpenDialogCommand.php | 17 --------- core/lib/Drupal/Core/Asset/AssetResolver.php | 20 ++--------- .../Core/Render/MainContent/DialogRenderer.php | 6 +++- .../Core/Render/MainContent/ModalRenderer.php | 6 +++- core/modules/history/src/Tests/HistoryTest.php | 4 +-- .../locale/src/Tests/LocaleLibraryAlterTest.php | 9 +++-- .../src/Tests/MenuLinkContentUITest.php | 4 +-- .../node/src/Tests/NodeTranslationUITest.php | 4 +-- core/modules/system/src/Tests/Ajax/DialogTest.php | 24 +++++++------ .../ajax_test/src/Form/AjaxTestDialogForm.php | 6 ++++ core/modules/views/includes/ajax.inc | 5 +++ .../views/src/Controller/ViewAjaxController.php | 40 ++++++++++------------ core/modules/views/src/Tests/ViewAjaxTest.php | 3 +- .../src/Unit/Controller/ViewAjaxControllerTest.php | 23 +++++-------- core/modules/views_ui/src/Tests/RowUITest.php | 16 +++++++-- 16 files changed, 91 insertions(+), 97 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index de417fa..df486c8 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1074,7 +1074,6 @@ services: library.discovery.parser: class: Drupal\Core\Asset\LibraryDiscoveryParser arguments: ['@app.root', '@module_handler'] - public: false library.dependency_resolver: class: Drupal\Core\Asset\LibraryDependencyResolver arguments: ['@library.discovery'] diff --git a/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php index 89ae38c..9ee09cc 100644 --- a/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php +++ b/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php @@ -125,9 +125,6 @@ public function setDialogTitle($title) { * Implements \Drupal\Core\Ajax\CommandInterface:render(). */ public function render() { - // Add the library for handling the dialog in the response. - $this->drupalAttachLibrary('core/drupal.dialog.ajax'); - // For consistency ensure the modal option is set to TRUE or FALSE. $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal']; return array( @@ -139,18 +136,4 @@ public function render() { ); } - /** - * Wraps drupal_render. - * - * @param string $name - * The name of the library. - * - * @todo Remove once drupal_render is converted to autoloadable code. - * @see https://drupal.org/node/2171071 - */ - protected function drupalAttachLibrary($name) { - $attached['#attached']['library'][] = $name; - drupal_process_attached($attached); - } - } diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php index f4a6bbe..9f17a5f 100644 --- a/core/lib/Drupal/Core/Asset/AssetResolver.php +++ b/core/lib/Drupal/Core/Asset/AssetResolver.php @@ -208,12 +208,6 @@ public function getCssAssets($optimize) { // Add the data to the CSS array depending on the type. switch ($options['type']) { - case 'inline': - // For inline stylesheets, we don't want to use the $data as the array - // key as $data could be a very long string of CSS. - $css[] = $options; - break; - case 'file': // Local CSS files are keyed by basename; if a file with the same // basename is added more than once, it gets overridden. @@ -398,17 +392,9 @@ protected function getJsAssets() { // Always add a tiny value to the weight, to conserve the insertion order. $options['weight'] += count($javascript) / 1000; - // Add the data to the CSS array depending on the type. - switch ($options['type']) { - case 'inline': - $javascript[] = $options; - break; - - default: // 'file' and 'external' - // Local and external files must keep their name as the associative key - // so the same JavaScript file is not added twice. - $javascript[$options['data']] = $options; - } + // Local and external files must keep their name as the associative key + // so the same JavaScript file is not added twice. + $javascript[$options['data']] = $options; } } } diff --git a/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php b/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php index 9f59944..7af9d93 100644 --- a/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php @@ -43,7 +43,11 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // First render the main content, because it might provide a title. $content = drupal_render_root($main_content); - drupal_process_attached($main_content); + + // Attach the library necessary for using the OpenDialogCommand and set the + // attachments for this Ajax response. + $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + $response->setAttachments($main_content['#attached']); // Determine the title: use the title provided by the main content if any, // otherwise get it from the routing information. diff --git a/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php b/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php index c971eb3..945a76c 100644 --- a/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php @@ -26,7 +26,11 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // First render the main content, because it might provide a title. $content = drupal_render_root($main_content); - drupal_process_attached($main_content); + + // Attach the library necessary for using the OpenModalDialogCommand and set + // the attachments for this Ajax response. + $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + $response->setAttachments($main_content['#attached']); // If the main content doesn't provide a title, use the title resolver. $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject()); diff --git a/core/modules/history/src/Tests/HistoryTest.php b/core/modules/history/src/Tests/HistoryTest.php index cdbfd68..995a717 100644 --- a/core/modules/history/src/Tests/HistoryTest.php +++ b/core/modules/history/src/Tests/HistoryTest.php @@ -119,8 +119,8 @@ function testHistory() { $this->drupalGet('node/' . $nid); // JavaScript present to record the node read. $settings = $this->getDrupalSettings(); - $this->assertTrue(isset($settings['ajaxPageState']['js']['core/modules/history/js/history.js']), 'history/api library is present.'); - $this->assertTrue(isset($settings['ajaxPageState']['js']['core/modules/history/js/mark-as-read.js']), 'history/mark-as-read library is present.'); + $libraries = explode(',', $settings['ajaxPageState']['libraries']); + $this->assertTrue(in_array('history/mark-as-read', $libraries), 'history/mark-as-read library is present.'); $this->assertEqual([$nid => TRUE], $settings['history']['nodesToMarkAsRead'], 'drupalSettings to mark node as read are present.'); // Simulate JavaScript: perform HTTP request to mark node as read. diff --git a/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php b/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php index 7b04c69..94eb895 100644 --- a/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php +++ b/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php @@ -30,10 +30,9 @@ class LocaleLibraryAlterTest extends WebTestBase { * @see locale_library_alter() */ public function testLibraryAlter() { - $attached['#attached']['library'][] = 'core/jquery.ui.datepicker'; - drupal_render($attached); - drupal_process_attached($attached); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.'); + $js_assets = $this->container->get('asset.resolver') + ->setLibraries(['core/jquery.ui.datepicker']) + ->getJsAssetsForHeader(FALSE); + $this->assertTrue(array_key_exists('core/modules/locale/locale.datepicker.js', $js_assets), 'locale.datepicker.js added to scripts.'); } } diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php index a876fb3..b063659 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php @@ -75,9 +75,9 @@ function testTranslationLinkTheme() { $edit['admin_theme'] = 'seven'; $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('admin/structure/menu/item/' . $entityId . '/edit'); - $this->assertRaw('"theme":"seven"', 'Edit uses admin theme.'); + $this->assertRaw('core/themes/seven/css/base/elements.css', 'Edit uses admin theme.'); $this->drupalGet('admin/structure/menu/item/' . $entityId . '/edit/translations'); - $this->assertRaw('"theme":"seven"', 'Translation uses admin theme as well.'); + $this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme as well.'); } } diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php index 153e87c..fe19cb2 100644 --- a/core/modules/node/src/Tests/NodeTranslationUITest.php +++ b/core/modules/node/src/Tests/NodeTranslationUITest.php @@ -172,13 +172,13 @@ function testTranslationLinkTheme() { $edit['use_admin_theme'] = TRUE; $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('node/' . $article->id() . '/translations'); - $this->assertRaw('"theme":"seven"', 'Translation uses admin theme if edit is admin.'); + $this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.'); // Turn off admin theme for editing, assert inheritance to translations. $edit['use_admin_theme'] = FALSE; $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('node/' . $article->id() . '/translations'); - $this->assertNoRaw('"theme":"seven"', 'Translation uses frontend theme if edit is frontend.'); + $this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.'); // Assert presence of translation page itself (vs. DisabledBundle below). $this->assertResponse(200); diff --git a/core/modules/system/src/Tests/Ajax/DialogTest.php b/core/modules/system/src/Tests/Ajax/DialogTest.php index cbb7093..6bf0058 100644 --- a/core/modules/system/src/Tests/Ajax/DialogTest.php +++ b/core/modules/system/src/Tests/Ajax/DialogTest.php @@ -94,7 +94,7 @@ public function testDialog() { // Emulate going to the JS version of the page and check the JSON response. $ajax_result = $this->drupalGetAJAX('ajax-test/dialog-contents', array(), array('Accept: application/vnd.drupal-modal')); - $this->assertEqual($modal_expected_response, $ajax_result[1], 'Modal dialog JSON response matches.'); + $this->assertEqual($modal_expected_response, $ajax_result[3], 'Modal dialog JSON response matches.'); // Check that requesting a "normal" dialog without JS goes to a page. $this->drupalGet('ajax-test/dialog-contents'); @@ -136,9 +136,13 @@ public function testDialog() { $ajax_result = $this->drupalPostAjaxForm('ajax-test/dialog', array(), 'button1'); // Check that CSS and JavaScript are "added" to the page dynamically. - $this->assertTrue(in_array('jquery.ui.dialog.css', array_keys($ajax_result[0]['settings']['ajaxPageState']['css'])), 'jQuery UI dialog CSS added to the page.'); - $this->assertTrue(in_array('core/assets/vendor/jquery.ui/ui/jquery.ui.dialog.js', array_keys($ajax_result[0]['settings']['ajaxPageState']['js'])), 'jQuery UI dialog JS added to the page.'); - $this->assertTrue(in_array('core/misc/dialog/dialog.ajax.js', array_keys($ajax_result[0]['settings']['ajaxPageState']['js'])), 'Drupal dialog JS added to the page.'); + $this->assertTrue(in_array('core/drupal.dialog.ajax', explode(',', $ajax_result[0]['settings']['ajaxPageState']['libraries'])), 'core/drupal.dialog.ajax library is added to the page.'); + $dialog_css_exists = strpos($ajax_result[1]['data'], 'jquery.ui.dialog.css') !== FALSE; + $this->assertTrue($dialog_css_exists, 'jQuery UI dialog CSS added to the page.'); + $dialog_js_exists = strpos($ajax_result[2]['data'], 'jquery.ui.dialog.js') !== FALSE; + $this->assertTrue($dialog_js_exists, 'jQuery UI dialog JS added to the page.'); + $dialog_js_exists = strpos($ajax_result[2]['data'], 'dialog.ajax.js') !== FALSE; + $this->assertTrue($dialog_js_exists, 'Drupal dialog JS added to the page.'); // Check that the response matches the expected value. $this->assertEqual($modal_expected_response, $ajax_result[3], 'POST request modal dialog JSON response matches.'); @@ -169,12 +173,12 @@ public function testDialog() { ], ]; $this->assertEqual($expected_ajax_settings, $ajax_result[0]['settings']['ajax']); - $this->drupalSetContent($ajax_result[1]['data']); + $this->drupalSetContent($ajax_result[3]['data']); // Remove the data, the form build id and token will never match. - unset($ajax_result[1]['data']); + unset($ajax_result[3]['data']); $form = $this->xpath("//form[@id='ajax-test-form']"); $this->assertTrue(!empty($form), 'Modal dialog JSON contains form.'); - $this->assertEqual($form_expected_response, $ajax_result[1]); + $this->assertEqual($form_expected_response, $ajax_result[3]); // Check that requesting an entity form dialog without JS goes to a page. $this->drupalGet('admin/structure/contact/add'); @@ -185,12 +189,12 @@ public function testDialog() { // Emulate going to the JS version of the form and check the JSON response. $ajax_result = $this->drupalGetAJAX('admin/structure/contact/add', array(), array('Accept: application/vnd.drupal-modal')); - $this->drupalSetContent($ajax_result[1]['data']); + $this->drupalSetContent($ajax_result[3]['data']); // Remove the data, the form build id and token will never match. - unset($ajax_result[1]['data']); + unset($ajax_result[3]['data']); $form = $this->xpath("//form[@id='contact-form-add-form']"); $this->assertTrue(!empty($form), 'Modal dialog JSON contains entity form.'); - $this->assertEqual($entity_form_expected_response, $ajax_result[1]); + $this->assertEqual($entity_form_expected_response, $ajax_result[3]); } } diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php index 946504d..c2a6a07 100644 --- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php +++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php @@ -101,6 +101,12 @@ protected function dialog($is_modal = FALSE) { $response = new AjaxResponse(); $title = $this->t('AJAX Dialog contents'); $html = drupal_render($content); + + // Attach the library necessary for using the Open(Modal)DialogCommand and + // set the attachments for this Ajax response. + $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; + $response->setAttachments($content['#attached']); + if ($is_modal) { $response->addCommand(new OpenModalDialogCommand($title, $html)); } diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc index d59ce61..f100583 100644 --- a/core/modules/views/includes/ajax.inc +++ b/core/modules/views/includes/ajax.inc @@ -40,6 +40,11 @@ function views_ajax_form_wrapper($form_class, FormStateInterface &$form_state) { // Ajax command list to execute. $response = new AjaxResponse(); + // Attach the library necessary for using the OpenModalDialogCommand and set + // the attachments for this Ajax response. + $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; + $response->setAttachments($form['#attached']); + $display = ''; $status_messages = array('#theme' => 'status_messages'); if ($messages = drupal_render($status_messages)) { diff --git a/core/modules/views/src/Controller/ViewAjaxController.php b/core/modules/views/src/Controller/ViewAjaxController.php index 401b0f9..e938ef8 100644 --- a/core/modules/views/src/Controller/ViewAjaxController.php +++ b/core/modules/views/src/Controller/ViewAjaxController.php @@ -8,9 +8,11 @@ namespace Drupal\views\Controller; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Render\RendererInterface; use Drupal\views\Ajax\ScrollTopCommand; use Drupal\views\Ajax\ViewAjaxResponse; use Drupal\views\ViewExecutableFactory; @@ -39,16 +41,26 @@ class ViewAjaxController implements ContainerInjectionInterface { protected $executableFactory; /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** * Constructs a ViewAjaxController object. * * @param \Drupal\Core\Entity\EntityStorageInterface $storage * The entity storage for views. * @param \Drupal\views\ViewExecutableFactory $executable_factory * The factory to load a view executable with. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ - public function __construct(EntityStorageInterface $storage, ViewExecutableFactory $executable_factory) { + public function __construct(EntityStorageInterface $storage, ViewExecutableFactory $executable_factory, RendererInterface $renderer) { $this->storage = $storage; $this->executableFactory = $executable_factory; + $this->renderer = $renderer; } /** @@ -57,7 +69,8 @@ public function __construct(EntityStorageInterface $storage, ViewExecutableFacto public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager')->getStorage('view'), - $container->get('views.executable') + $container->get('views.executable'), + $container->get('renderer') ); } @@ -96,7 +109,7 @@ public function ajaxView(Request $request) { // Remove all of this stuff from the query of the request so it doesn't // end up in pagers and tablesort URLs. - foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state') as $key) { + foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids') as $key) { $request->query->remove($key); $request->request->remove($key); } @@ -138,7 +151,8 @@ public function ajaxView(Request $request) { $view->dom_id = $dom_id; if ($preview = $view->preview($display_id, $args)) { - $response->addCommand(new ReplaceCommand(".view-dom-id-$dom_id", $this->drupalRender($preview))); + $response->addCommand(new ReplaceCommand(".view-dom-id-$dom_id", $this->renderer->render($preview))); + $response->setAttachments($preview['#attached']); } return $response; } @@ -151,22 +165,4 @@ public function ajaxView(Request $request) { } } - /** - * Wraps drupal_render. - * - * @param array $elements - * The structured array describing the data to be rendered. - * - * @return string - * The rendered HTML. - * - * @todo Remove once drupal_render is converted to autoloadable code. - * @see https://drupal.org/node/2171071 - */ - protected function drupalRender(array $elements) { - $output = drupal_render($elements); - drupal_process_attached($elements); - return $output; - } - } diff --git a/core/modules/views/src/Tests/ViewAjaxTest.php b/core/modules/views/src/Tests/ViewAjaxTest.php index 0420a0a..5eb004d 100644 --- a/core/modules/views/src/Tests/ViewAjaxTest.php +++ b/core/modules/views/src/Tests/ViewAjaxTest.php @@ -50,7 +50,8 @@ public function testAjaxView() { 'view_name' => 'test_ajax_view', 'view_display_id' => 'page_1', ); - $response = $this->drupalPost('views/ajax', 'application/json', $post); + $post += $this->getAjaxPageStatePostData(); + $response = $this->drupalPost('views/ajax', 'application/vnd.drupal-ajax', $post); $data = Json::decode($response); // Ensure that the view insert command is part of the result. diff --git a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php index 996ac1a..7e09cb4 100644 --- a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php +++ b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php @@ -45,8 +45,15 @@ protected function setUp() { $this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory') ->disableOriginalConstructor() ->getMock(); - - $this->viewAjaxController = new TestViewAjaxController($this->viewStorage, $this->executableFactory); + $this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); + $this->renderer->expects($this->any()) + ->method('render') + ->will($this->returnCallback(function(array &$elements) { + $elements['#attached'] = []; + return isset($elements['#markup']) ? $elements['#markup'] : ''; + })); + + $this->viewAjaxController = new ViewAjaxController($this->viewStorage, $this->executableFactory, $this->renderer); } /** @@ -289,18 +296,6 @@ protected function assertViewResultCommand(ViewAjaxResponse $response, $position } -/** - * Overrides ViewAjaxController::drupalRender to protect the parent method. - */ -class TestViewAjaxController extends ViewAjaxController { - - // @todo Remove once drupal_render is converted to autoloadable code. - protected function drupalRender(array $elements) { - return isset($elements['#markup']) ? $elements['#markup'] : ''; - } - -} - } namespace { diff --git a/core/modules/views_ui/src/Tests/RowUITest.php b/core/modules/views_ui/src/Tests/RowUITest.php index 67e415d..eceb3bf 100644 --- a/core/modules/views_ui/src/Tests/RowUITest.php +++ b/core/modules/views_ui/src/Tests/RowUITest.php @@ -60,8 +60,20 @@ public function testRowUI() { $this->assertEqual($row['options']['test_option'], $random_name, 'Make sure that the custom settings field got saved as expected.'); // Change the row plugin to fields using ajax. - $this->drupalPostAjaxForm($row_plugin_url, array('row[type]' => 'fields'), array('op' => 'Apply'), str_replace('/nojs/', '/ajax/', $row_plugin_url)); - $this->drupalPostAjaxForm(NULL, array(), array('op' => 'Apply'), str_replace('/nojs/', '/ajax/', $row_plugin_url)); + // Note: this is the best approximation we can achieve, because we cannot + // simulate the 'openDialog' command in + // WebTestBase::drupalProcessAjaxResponse(), hence we have to make do. + $row_plugin_url_ajax = str_replace('/nojs/', '/ajax/', $row_plugin_url); + $ajax_settings = [ + 'accepts' => 'application/vnd.drupal-ajax', + 'submit' => [ + '_triggering_element_name' => 'op', + '_triggering_element_value' => 'Apply', + ], + 'url' => $row_plugin_url_ajax, + ]; + $this->drupalPostAjaxForm($row_plugin_url, ['row[type]' => 'fields'], NULL, $row_plugin_url_ajax, [], [], NULL, $ajax_settings); + $this->drupalGet($row_plugin_url); $this->assertResponse(200); $this->assertFieldByName('row[type]', 'fields', 'Make sure that the fields got saved as used row plugin.'); }