Commit 3467f054 authored by webchick's avatar webchick

Issue #2121713 by tim.plunkett, dawehner, rteijeiro, jiv_e: Move...

Issue #2121713 by tim.plunkett, dawehner, rteijeiro, jiv_e: Move drupal_html_id() and drupal_html_class() to Drupal\Component\Utility.
parent db30eaf4
......@@ -564,8 +564,8 @@ services:
tags:
- { name: event_subscriber }
arguments: ['@settings']
ajax_response_subscriber:
class: Drupal\Core\EventSubscriber\AjaxResponseSubscriber
ajax_subscriber:
class: Drupal\Core\EventSubscriber\AjaxSubscriber
tags:
- { name: event_subscriber }
route_enhancer.param_conversion:
......
......@@ -13,6 +13,7 @@
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SortArray;
......@@ -1299,25 +1300,12 @@ function drupal_clear_css_cache() {
*
* @return
* The cleaned identifier.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.0.
* Use \Drupal\Component\Utility\Html::cleanCssIdentifier()
*/
function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '__' => '__', '/' => '-', '[' => '-', ']' => '')) {
// By default, we filter using Drupal's coding standards.
$identifier = strtr($identifier, $filter);
// Valid characters in a CSS identifier are:
// - the hyphen (U+002D)
// - a-z (U+0030 - U+0039)
// - A-Z (U+0041 - U+005A)
// - the underscore (U+005F)
// - 0-9 (U+0061 - U+007A)
// - ISO 10646 characters U+00A1 and higher
// We strip out any character not in the above list.
$identifier = preg_replace('/[^\x{002D}\x{0030}-\x{0039}\x{0041}-\x{005A}\x{005F}\x{0061}-\x{007A}\x{00A1}-\x{FFFF}]/u', '', $identifier);
// Identifiers cannot start with a digit, two hyphens, or a hyphen followed by a digit.
$identifier = preg_replace(array('/^[0-9]/', '/^(-[0-9])|^(--)/'), array('_', '__') , $identifier);
return $identifier;
return Html::cleanCssIdentifier($identifier, $filter);
}
/**
......@@ -1331,16 +1319,12 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
*
* @return
* The cleaned class name.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.0.
* Use \Drupal\Component\Utility\Html::getClass()
*/
function drupal_html_class($class) {
// The output of this function will never change, so this uses a normal
// static instead of drupal_static().
static $classes = array();
if (!isset($classes[$class])) {
$classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
}
return $classes[$class];
return Html::getClass($class);
}
/**
......@@ -1369,66 +1353,12 @@ function drupal_html_class($class) {
*
* @return
* The cleaned ID.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.0.
* Use \Drupal\Component\Utility\Html::getUniqueId()
*/
function drupal_html_id($id) {
// If this is an Ajax request, then content returned by this page request will
// be merged with content already on the base page. The HTML IDs must be
// unique for the fully merged content. Therefore, initialize $seen_ids to
// take into account IDs that are already in use on the base page.
$seen_ids_init = &drupal_static(__FUNCTION__ . ':init');
if (!isset($seen_ids_init)) {
$ajax_html_ids = \Drupal::request()->request->get('ajax_html_ids');
// Ideally, Drupal would provide an API to persist state information about
// prior page requests in the database, and we'd be able to add this
// function's $seen_ids static variable to that state information in order
// to have it properly initialized for this page request. However, no such
// page state API exists, so instead, ajax.js adds all of the in-use HTML
// IDs to the POST data of Ajax submissions. Direct use of $_POST is
// normally not recommended as it could open up security risks, but because
// the raw POST data is cast to a number before being returned by this
// function, this usage is safe.
if (empty($ajax_html_ids)) {
$seen_ids_init = array();
}
else {
// This function ensures uniqueness by appending a counter to the base id
// requested by the calling function after the first occurrence of that
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
// returned by this function, potentially with the appended counter, so
// we parse that to reconstruct the $seen_ids array.
$ajax_html_ids = explode(' ', $ajax_html_ids);
foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
if (!empty($parts[1]) && is_numeric($parts[1])) {
list($seen_id, $i) = $parts;
}
else {
$i = 1;
}
if (!isset($seen_ids_init[$seen_id]) || ($i > $seen_ids_init[$seen_id])) {
$seen_ids_init[$seen_id] = $i;
}
}
}
}
$seen_ids = &drupal_static(__FUNCTION__, $seen_ids_init);
$id = drupal_clean_id_identifier($id);
// Ensure IDs are unique by appending a counter after the first occurrence.
// The counter needs to be appended with a delimiter that does not exist in
// the base ID. Requiring a unique delimiter helps ensure that we really do
// return unique IDs and also helps us re-create the $seen_ids array during
// Ajax requests.
if (isset($seen_ids[$id])) {
$id = $id . '--' . ++$seen_ids[$id];
}
else {
$seen_ids[$id] = 1;
}
return $id;
return Html::getUniqueId($id);
}
/**
......@@ -1444,21 +1374,12 @@ function drupal_html_id($id) {
* The cleaned ID.
*
* @see drupal_html_id()
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.0.
* Use \Drupal\Component\Utility\Html::getId()
*/
function drupal_clean_id_identifier($id) {
$id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
// As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
// only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
// colons (":"), and periods ("."). We strip out any character not in that
// list. Note that the CSS spec doesn't allow colons or periods in identifiers
// (http://www.w3.org/TR/CSS21/syndata.html#characters), so we strip those two
// characters as well.
$id = preg_replace('/[^A-Za-z0-9\-_]/', '', $id);
// Removing multiple consecutive hyphens.
$id = preg_replace('/\-+/', '-', $id);
return $id;
return Html::getId($id);
}
/**
......
......@@ -14,6 +14,229 @@
*/
class Html {
/**
* An array of previously cleaned HTML classes.
*
* @var array
*/
protected static $classes = array();
/**
* An array of the initial IDs used in one request.
*
* @var array
*/
protected static $seenIdsInit;
/**
* An array of IDs, including incremented versions when an ID is duplicated.
* @var array
*/
protected static $seenIds;
/**
* Contains the current AJAX HTML IDs.
*
* @var string
*/
protected static $ajaxHTMLIDs;
/**
* Prepares a string for use as a valid class name.
*
* Do not pass one string containing multiple classes as they will be
* incorrectly concatenated with dashes, i.e. "one two" will become "one-two".
*
* @param string $class
* The class name to clean.
*
* @return string
* The cleaned class name.
*/
public static function getClass($class) {
if (!isset(static::$classes[$class])) {
static::$classes[$class] = static::cleanCssIdentifier(Unicode::strtolower($class));
}
return static::$classes[$class];
}
/**
* Prepares a string for use as a CSS identifier (element, class, or ID name).
*
* http://www.w3.org/TR/CSS21/syndata.html#characters shows the syntax for
* valid CSS identifiers (including element names, classes, and IDs in
* selectors.)
*
* @param string $identifier
* The identifier to clean.
* @param array $filter
* An array of string replacements to use on the identifier.
*
* @return string
* The cleaned identifier.
*/
public static function cleanCssIdentifier($identifier, array $filter = array(
' ' => '-',
'_' => '-',
'__' => '__',
'/' => '-',
'[' => '-',
']' => ''
)) {
$identifier = strtr($identifier, $filter);
// Valid characters in a CSS identifier are:
// - the hyphen (U+002D)
// - a-z (U+0030 - U+0039)
// - A-Z (U+0041 - U+005A)
// - the underscore (U+005F)
// - 0-9 (U+0061 - U+007A)
// - ISO 10646 characters U+00A1 and higher
// We strip out any character not in the above list.
$identifier = preg_replace('/[^\x{002D}\x{0030}-\x{0039}\x{0041}-\x{005A}\x{005F}\x{0061}-\x{007A}\x{00A1}-\x{FFFF}]/u', '', $identifier);
// Identifiers cannot start with a digit, two hyphens, or a hyphen followed by a digit.
$identifier = preg_replace(array(
'/^[0-9]/',
'/^(-[0-9])|^(--)/'
), array('_', '__'), $identifier);
return $identifier;
}
/**
* Sets the AJAX HTML IDs.
*
* @param string $ajax_html_ids
* The AJAX HTML IDs, probably coming from the current request.
*/
public static function setAjaxHtmlIds($ajax_html_ids = '') {
static::$ajaxHTMLIDs = $ajax_html_ids;
}
/**
* Prepares a string for use as a valid HTML ID and guarantees uniqueness.
*
* This function ensures that each passed HTML ID value only exists once on
* the page. By tracking the already returned ids, this function enables
* forms, blocks, and other content to be output multiple times on the same
* page, without breaking (X)HTML validation.
*
* For already existing IDs, a counter is appended to the ID string.
* Therefore, JavaScript and CSS code should not rely on any value that was
* generated by this function and instead should rely on manually added CSS
* classes or similarly reliable constructs.
*
* Two consecutive hyphens separate the counter from the original ID. To
* manage uniqueness across multiple Ajax requests on the same page, Ajax
* requests POST an array of all IDs currently present on the page, which are
* used to prime this function's cache upon first invocation.
*
* To allow reverse-parsing of IDs submitted via Ajax, any multiple
* consecutive hyphens in the originally passed $id are replaced with a
* single hyphen.
*
* @param string $id
* The ID to clean.
*
* @return string
* The cleaned ID.
*/
public static function getUniqueId($id) {
// If this is an Ajax request, then content returned by this page request
// will be merged with content already on the base page. The HTML IDs must
// be unique for the fully merged content. Therefore, initialize $seen_ids
// to take into account IDs that are already in use on the base page.
if (!isset(static::$seenIdsInit)) {
// Ideally, Drupal would provide an API to persist state information about
// prior page requests in the database, and we'd be able to add this
// function's $seen_ids static variable to that state information in order
// to have it properly initialized for this page request. However, no such
// page state API exists, so instead, ajax.js adds all of the in-use HTML
// IDs to the POST data of Ajax submissions. Direct use of $_POST is
// normally not recommended as it could open up security risks, but
// because the raw POST data is cast to a number before being returned by
// this function, this usage is safe.
if (empty(static::$ajaxHTMLIDs)) {
static::$seenIdsInit = array();
}
else {
// This function ensures uniqueness by appending a counter to the base
// id requested by the calling function after the first occurrence of
// that requested id. $_POST['ajax_html_ids'] contains the ids as they
// were returned by this function, potentially with the appended
// counter, so we parse that to reconstruct the $seen_ids array.
$ajax_html_ids = explode(' ', static::$ajaxHTMLIDs);
foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
if (!empty($parts[1]) && is_numeric($parts[1])) {
list($seen_id, $i) = $parts;
}
else {
$i = 1;
}
if (!isset(static::$seenIdsInit[$seen_id]) || ($i > static::$seenIdsInit[$seen_id])) {
static::$seenIdsInit[$seen_id] = $i;
}
}
}
}
if (!isset(static::$seenIds)) {
static::$seenIds = static::$seenIdsInit;
}
$id = static::getId($id);
// Ensure IDs are unique by appending a counter after the first occurrence.
// The counter needs to be appended with a delimiter that does not exist in
// the base ID. Requiring a unique delimiter helps ensure that we really do
// return unique IDs and also helps us re-create the $seen_ids array during
// Ajax requests.
if (isset(static::$seenIds[$id])) {
$id = $id . '--' . ++static::$seenIds[$id];
}
else {
static::$seenIds[$id] = 1;
}
return $id;
}
/**
* Prepares a string for use as a valid HTML ID.
*
* Only use this function when you want to intentionally skip the uniqueness
* guarantee of self::getUniqueId().
*
* @param string $id
* The ID to clean.
*
* @return string
* The cleaned ID.
*
* @see self::getUniqueId()
*/
public static function getId($id) {
$id = strtr(Unicode::strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
// As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
// only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
// colons (":"), and periods ("."). We strip out any character not in that
// list. Note that the CSS spec doesn't allow colons or periods in identifiers
// (http://www.w3.org/TR/CSS21/syndata.html#characters), so we strip those two
// characters as well.
$id = preg_replace('/[^A-Za-z0-9\-_]/', '', $id);
// Removing multiple consecutive hyphens.
$id = preg_replace('/\-+/', '-', $id);
return $id;
}
/**
* Resets the list of seen IDs.
*/
public static function resetSeenIds() {
static::$seenIds = NULL;
}
/**
* Normalizes an HTML snippet.
*
......
......@@ -2,18 +2,32 @@
/**
* @file
* Contains \Drupal\Core\EventSubscriber\AjaxResponseSubscriber.
* Contains \Drupal\Core\EventSubscriber\AjaxSubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Drupal\Component\Utility\Html;
use Drupal\Core\Ajax\AjaxResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class AjaxResponseSubscriber implements EventSubscriberInterface {
/**
* Subscribes to set AJAX HTML IDs and prepare AJAX responses.
*/
class AjaxSubscriber implements EventSubscriberInterface {
/**
* Sets the AJAX HTML IDs from the current request.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The response event, which contains the current request.
*/
public function onRequest(GetResponseEvent $event) {
Html::setAjaxHtmlIds($event->getRequest()->request->get('ajax_html_ids', ''));
}
/**
* Renders the ajax commands right before preparing the result.
......@@ -33,6 +47,7 @@ public function onResponse(FilterResponseEvent $event) {
*/
public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = array('onResponse', -100);
$events[KernelEvents::REQUEST][] = array('onREquest', 50);
return $events;
}
......
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Form;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\UrlHelper;
......@@ -384,10 +385,10 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
// Assign a default CSS class name based on $form_id.
// This happens here and not in self::prepareForm() in order to allow the
// form constructor function to override or remove the default class.
$form['#attributes']['class'][] = $this->drupalHtmlClass($form_id);
$form['#attributes']['class'][] = Html::getClass($form_id);
// Same for the base form ID, if any.
if (isset($form_state['build_info']['base_form_id'])) {
$form['#attributes']['class'][] = $this->drupalHtmlClass($form_state['build_info']['base_form_id']);
$form['#attributes']['class'][] = Html::getClass($form_state['build_info']['base_form_id']);
}
// We need to pass $form_state by reference in order for forms to modify it,
......@@ -459,7 +460,7 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
// element IDs needlessly.
if (!FormState::hasAnyErrors()) {
// In case of errors, do not break HTML IDs of other forms.
$this->drupalStaticReset('drupal_html_id');
Html::resetSeenIds();
}
if (!$form_state['rebuild'] && !FormState::hasAnyErrors()) {
......@@ -561,7 +562,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
else {
$form['#token'] = $form_id;
$form['form_token'] = array(
'#id' => $this->drupalHtmlId('edit-' . $form_id . '-form-token'),
'#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
'#type' => 'token',
'#default_value' => $this->csrfToken->get($form['#token']),
// Form processing and validation requires this value, so ensure the
......@@ -576,7 +577,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
$form['form_id'] = array(
'#type' => 'hidden',
'#value' => $form_id,
'#id' => $this->drupalHtmlId("edit-$form_id"),
'#id' => Html::getUniqueId("edit-$form_id"),
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
......@@ -584,7 +585,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
);
}
if (!isset($form['#id'])) {
$form['#id'] = $this->drupalHtmlId($form_id);
$form['#id'] = Html::getUniqueId($form_id);
}
$form += $this->getElementInfo('form');
......@@ -702,7 +703,7 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
}
if (!isset($element['#id'])) {
$element['#id'] = $this->drupalHtmlId('edit-' . implode('-', $element['#parents']));
$element['#id'] = Html::getUniqueId('edit-' . implode('-', $element['#parents']));
}
// Add the aria-describedby attribute to associate the form control with its
......@@ -1084,31 +1085,6 @@ protected function getElementInfo($type) {
return element_info($type);
}
/**
* Wraps drupal_html_class().
*
* @return string
*/
protected function drupalHtmlClass($class) {
return drupal_html_class($class);
}
/**
* Wraps drupal_html_id().
*
* @return string
*/
protected function drupalHtmlId($id) {
return drupal_html_id($id);
}
/**
* Wraps drupal_static_reset().
*/
protected function drupalStaticReset($name = NULL) {
drupal_static_reset($name);
}
/**
* Gets the current active user.
*
......
......@@ -7,6 +7,7 @@
namespace Drupal\block\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\UrlCacheContext;
use Drupal\simpletest\DrupalUnitTestBase;
......@@ -94,7 +95,7 @@ public function testBasicRendering() {
$this->assertEqual(drupal_render($output), $expected_output);
// Reset the HTML IDs so that the next render is not affected.
drupal_static_reset('drupal_html_id');
Html::resetSeenIds();
// Test the rendering of a block with a given title.
$entity = $this->controller->create(array(
......
<?php
/**
* @file
* Definition of Drupal\system\Tests\Common\HtmlIdentifierUnitTest.
*/
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\KernelTestBase;
/**
* Tests the functions drupal_html_class(), drupal_html_id() and
* drupal_clean_css_identifier() for expected behavior.
*
* @group Common
*/
class HtmlIdentifierUnitTest extends KernelTestBase {
/**
* Tests that drupal_clean_css_identifier() cleans the identifier properly.
*/
function testDrupalCleanCSSIdentifier() {
// Verify that no valid ASCII characters are stripped from the identifier.
$identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
$this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, 'Verify valid ASCII characters pass through.');
// Verify that valid UTF-8 characters are not stripped from the identifier.
$identifier = '¡¢£¤¥';
$this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, 'Verify valid UTF-8 characters pass through.');
// Verify that invalid characters (including non-breaking space) are stripped from the identifier.
$this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', 'Strip invalid characters.');
// Verify that double underscores are not stripped from the identifier.
$identifier = 'css__identifier__with__double__underscores';
$this->assertIdentical(drupal_clean_css_identifier($identifier), $identifier, 'Verify double underscores pass through.');
// Verify that an identifier starting with a digit is replaced.
$this->assertIdentical(drupal_clean_css_identifier('1cssidentifier', array()), '_cssidentifier', 'Verify identifier starting with a digit is replaced.');
// Verify that an identifier starting with a hyphen followed by a digit is
// replaced.
$this->assertIdentical(drupal_clean_css_identifier('-1cssidentifier', array()), '__cssidentifier', 'Verify identifier starting with a hyphen followed by a digit is replaced.');
// Verify that an identifier starting with two hyphens is replaced.
$this->assertIdentical(drupal_clean_css_identifier('--cssidentifier', array()), '__cssidentifier', 'Verify identifier starting with two hyphens is replaced.');
}
/**
* Tests that drupal_html_class() cleans the class name properly.
*/
function testDrupalHTMLClass() {
// Verify Drupal coding standards are enforced.
$this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', 'Enforce Drupal coding standards.');
}
/**
* Tests that drupal_html_id() cleans the ID properly.
*/
function testDrupalHTMLId() {
// Verify that letters, digits, and hyphens are not stripped from the ID.
$id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
$this->assertIdentical(drupal_html_id($id), $id, 'Verify valid characters pass through.');
// Verify that invalid characters are stripped from the ID.
$this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', 'Strip invalid characters.');
// Verify Drupal coding standards are enforced.
$this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', 'Enforce Drupal coding standards.');
// Reset the static cache so we can ensure the unique id count is at zero.
drupal_static_reset('drupal_html_id');
// Clean up IDs with invalid starting characters.
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', 'Test the uniqueness of IDs #1.');
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', 'Test the uniqueness of IDs #2.');
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', 'Test the uniqueness of IDs #3.');
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\views_ui\Form\Ajax;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
......@@ -96,8 +97,7 @@ public function getForm(ViewStorageInterface $view, $display_id, $js) {
// Reset the cache of IDs. Drupal rather aggressively prevents ID
// duplication but this causes it to remember IDs that are no longer even
// being used.
$seen_ids_init = &drupal_static('drupal_html_id:init');
$seen_ids_init = array();
Html::resetSeenIds();
// check to see if this is the top form of the stack. If it is, pop
// it off; if it isn't, the user clicked somewhere else and the stack is
......
......@@ -61,7 +61,7 @@ public function testRowUI() {
// 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'));
$this->drupalPostAjaxForm(NULL, array(), array('op' => 'Apply'), str_replace('/nojs/', '/ajax/', $row_plugin_url));
$this->assertResponse(200);
$this->assertFieldByName('row[type]', 'fields', 'Make sure that the fields got saved as used row plugin.');
}
......
......@@ -8,6 +8,7 @@
namespace Drupal\views_ui;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Timer;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
......@@ -433,8 +434,7 @@ public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALS
// Reset the cache of IDs. Drupal rather aggressively prevents ID
// duplication but this causes it to remember IDs that are no longer even
// being used.
$seen_ids_init = &drupal_static('drupal_html_id:init');
$seen_ids_init = array();
Html::resetSeenIds();
if (empty($this->stack)) {
$this->stack = array();
......
......@@ -205,7 +205,7 @@ public function testGetFormWithString() {
$form = $this->formBuilder->getForm($form_id);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame($form_id, $form['#id']);
$this->assertSame('test-form-id', $form['#id']);
}
/**
......@@ -219,7 +219,7 @@ public function testGetFormWithObject() {
$form = $this->formBuilder->getForm($form_arg);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame($form_id, $form['#id']);
$this->assertArrayHasKey('#id', $form);
}
/**
......@@ -234,7 +234,7 @@ public function testGetFormWithClassString() {
$form = $this->formBuilder->getForm($form_id);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame('test_form', $form['#id']);
$this->assertSame('test-form', $form['#id']);
}
/**
......@@ -249,7 +249,7 @@ public function testBuildFormWithString() {
$form = $this->formBuilder->getForm($form_id);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame($form_id, $form['#id']);
$this->assertArrayHasKey('#id', $form);
}
/**
......@@ -264,7 +264,7 @@ public function testBuildFormWithClassString() {
$form = $this->formBuilder->buildForm($form_id, $form_state);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame('test_form', $form['#id']);
$this->assertSame('test-form', $form['#id']);
}
/**
......@@ -280,7 +280,7 @@ public function testBuildFormWithObject() {
$form = $this->formBuilder->buildForm($form_arg, $form_state);