diff --git a/core/includes/common.inc b/core/includes/common.inc index 1de05bf..137899f 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1531,7 +1531,8 @@ function drupal_delete_file_if_stale($uri) { * @return * The cleaned identifier. * - * @deprecated as of Drupal 8.0. Use \Drupal\Component\Utility\Html::cleanCssIdentifier() + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal\Component\Utility\Html::cleanCssIdentifier() */ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '__' => '__', '/' => '-', '[' => '-', ']' => '')) { return Html::cleanCssIdentifier($identifier, $filter); @@ -1549,7 +1550,8 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_ * @return * The cleaned class name. * - * @deprecated as of Drupal 8.0. Use \Drupal\Component\Utility\Html::getClass() + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal\Component\Utility\Html::getClass() */ function drupal_html_class($class) { return Html::getClass($class); @@ -1582,10 +1584,11 @@ function drupal_html_class($class) { * @return * The cleaned ID. * - * @deprecated as of Drupal 8.0. Use \Drupal\Component\Utility\Html::getId() + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal\Component\Utility\Html::getUniqueId() */ function drupal_html_id($id) { - return Html::getId($id); + return Html::getUniqueId($id); } /** @@ -1601,21 +1604,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. + * 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); } /** diff --git a/core/lib/Drupal/Component/Utility/Html.php b/core/lib/Drupal/Component/Utility/Html.php index fc5d621..04ce629 100644 --- a/core/lib/Drupal/Component/Utility/Html.php +++ b/core/lib/Drupal/Component/Utility/Html.php @@ -122,7 +122,7 @@ public static function cleanCssIdentifier($identifier, array $filter = array( * @return string * The cleaned ID. */ - public static function getId($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 @@ -166,21 +166,9 @@ public static function getId($id) { if (!isset(static::$seenIds)) { static::$seenIds = static::$seenIdsInit; } - $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); + + $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 @@ -196,6 +184,36 @@ public static function getId($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() { diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index f58ee47..e299395 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -699,7 +699,7 @@ public function prepareForm($form_id, &$form, &$form_state) { else { $form['#token'] = $form_id; $form['form_token'] = array( - '#id' => Html::getId('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 @@ -714,7 +714,7 @@ public function prepareForm($form_id, &$form, &$form_state) { $form['form_id'] = array( '#type' => 'hidden', '#value' => $form_id, - '#id' => Html::getId("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. @@ -722,7 +722,7 @@ public function prepareForm($form_id, &$form, &$form_state) { ); } if (!isset($form['#id'])) { - $form['#id'] = Html::getId($form_id); + $form['#id'] = Html::getUniqueId($form_id); } $form += $this->getElementInfo('form'); @@ -1001,7 +1001,7 @@ public function doBuildForm($form_id, &$element, &$form_state) { } if (!isset($element['#id'])) { - $element['#id'] = Html::getId('edit-' . implode('-', $element['#parents'])); + $element['#id'] = Html::getUniqueId('edit-' . implode('-', $element['#parents'])); } // Add the aria-describedby attribute to associate the form control with its diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php index 5faf5a0..02dd002 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -7,6 +7,7 @@ namespace Drupal\views_ui; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Timer; use Drupal\Component\Utility\Xss; diff --git a/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php b/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php index fc79a41..5ebd0bc 100644 --- a/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php @@ -25,7 +25,7 @@ class HtmlTest extends UnitTestCase { public static function getInfo() { return array( 'name' => 'HTML identifiers', - 'description' => 'Test the methods Html::getClass(), Html::getId() and Html::cleanCssIdentifier() for expected behavior.', + 'description' => 'Test the methods Html::getClass(), Html::getUniqueId() and Html::cleanCssIdentifier() for expected behavior.', 'group' => 'Common', ); } @@ -94,7 +94,7 @@ public function testHtmlClass() { } /** - * Tests the Html::getId() method. + * Tests the Html::getUniqueId() method. * * @param string $expected * The expected result. @@ -103,14 +103,52 @@ public function testHtmlClass() { * @param bool $reset * (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE. * + * @dataProvider providerTestHtmlGetUniqueId + * + * @covers ::getUniqueId + */ + public function testHtmlGetUniqueId($expected, $source, $reset = FALSE) { + if ($reset) { + Html::resetSeenIds(); + } + $this->assertSame($expected, Html::getUniqueId($source)); + } + + /** + * Provides test data for testHtmlGetId(). + * + * @return array + * Test data. + */ + public function providerTestHtmlGetUniqueId() { + $id = 'abcdefghijklmnopqrstuvwxyz-0123456789'; + return array( + // Verify that letters, digits, and hyphens are not stripped from the ID. + array($id, $id), + // Verify that invalid characters are stripped from the ID. + array('invalididentifier', 'invalid,./:@\\^`{Üidentifier'), + // Verify Drupal coding standards are enforced. + array('id-name-1', 'ID NAME_[1]'), + // Verify that a repeated ID is made unique. + array('test-unique-id', 'test-unique-id', TRUE), + array('test-unique-id--2', 'test-unique-id'), + array('test-unique-id--3', 'test-unique-id'), + ); + } + + /** + * Tests the Html::getUniqueId() method. + * + * @param string $expected + * The expected result. + * @param string $source + * The string being transformed to an ID. + * * @dataProvider providerTestHtmlGetId * * @covers ::getId */ - public function testHtmlGetId($expected, $source, $reset = FALSE) { - if ($reset) { - Html::resetSeenIds(); - } + public function testHtmlGetId($expected, $source) { $this->assertSame($expected, Html::getId($source)); } @@ -130,9 +168,8 @@ public function providerTestHtmlGetId() { // Verify Drupal coding standards are enforced. array('id-name-1', 'ID NAME_[1]'), // Verify that a repeated ID is made unique. - array('test-unique-id', 'test-unique-id', TRUE), - array('test-unique-id--2', 'test-unique-id'), - array('test-unique-id--3', 'test-unique-id'), + array('test-unique-id', 'test-unique-id'), + array('test-unique-id', 'test-unique-id'), ); }