summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2014-08-29 04:34:31 (GMT)
committerwebchick2014-08-29 04:34:31 (GMT)
commit3467f054160f946975f1e129e79304e2a9fa8407 (patch)
tree02304476283d5dae92b4483785cd4c13f2960981
parentdb30eaf4381cbd681235cc8be0804e47199017a6 (diff)
Issue #2121713 by tim.plunkett, dawehner, rteijeiro, jiv_e: Move drupal_html_id() and drupal_html_class() to Drupal\Component\Utility.
-rw-r--r--core/core.services.yml4
-rw-r--r--core/includes/common.inc113
-rw-r--r--core/lib/Drupal/Component/Utility/Html.php223
-rw-r--r--core/lib/Drupal/Core/EventSubscriber/AjaxSubscriber.php (renamed from core/lib/Drupal/Core/EventSubscriber/AjaxResponseSubscriber.php)21
-rw-r--r--core/lib/Drupal/Core/Form/FormBuilder.php40
-rw-r--r--core/modules/block/src/Tests/BlockViewBuilderTest.php3
-rw-r--r--core/modules/system/src/Tests/Common/HtmlIdentifierUnitTest.php79
-rw-r--r--core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php4
-rw-r--r--core/modules/views_ui/src/Tests/RowUITest.php2
-rw-r--r--core/modules/views_ui/src/ViewUI.php4
-rw-r--r--core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php16
-rw-r--r--core/tests/Drupal/Tests/Core/Form/FormTestBase.php3
-rw-r--r--core/tests/Drupal/Tests/Core/Utility/HtmlTest.php213
13 files changed, 498 insertions, 227 deletions
diff --git a/core/core.services.yml b/core/core.services.yml
index b68dd90..cad6d48 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -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:
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 3b07dd7..beaf75e 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -13,6 +13,7 @@ use Drupal\Component\Serialization\Yaml;
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);
}
/**
diff --git a/core/lib/Drupal/Component/Utility/Html.php b/core/lib/Drupal/Component/Utility/Html.php
index 2b5455f..4dd9d27 100644
--- a/core/lib/Drupal/Component/Utility/Html.php
+++ b/core/lib/Drupal/Component/Utility/Html.php
@@ -15,6 +15,229 @@ namespace Drupal\Component\Utility;
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.
*
* This function is essentially \DOMDocument::normalizeDocument(), but
diff --git a/core/lib/Drupal/Core/EventSubscriber/AjaxResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AjaxSubscriber.php
index cf4dca1..cc260b6 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AjaxResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AjaxSubscriber.php
@@ -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 @@ class AjaxResponseSubscriber implements EventSubscriberInterface {
*/
public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = array('onResponse', -100);
+ $events[KernelEvents::REQUEST][] = array('onREquest', 50);
return $events;
}
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 429aceb..b1e2260 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
// 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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
// 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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
$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 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
);
}
if (!isset($form['#id'])) {
- $form['#id'] = $this->drupalHtmlId($form_id);
+ $form['#id'] = Html::getUniqueId($form_id);
}
$form += $this->getElementInfo('form');
@@ -702,7 +703,7 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
}
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
@@ -1085,31 +1086,6 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
}
/**
- * 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.
*
* @return \Drupal\Core\Session\AccountInterface
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index be26a86..58becb0 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -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 @@ class BlockViewBuilderTest extends DrupalUnitTestBase {
$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(
diff --git a/core/modules/system/src/Tests/Common/HtmlIdentifierUnitTest.php b/core/modules/system/src/Tests/Common/HtmlIdentifierUnitTest.php
deleted file mode 100644
index 74775ea..0000000
--- a/core/modules/system/src/Tests/Common/HtmlIdentifierUnitTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?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.');
- }
-}
diff --git a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
index 8275a48..15e6ee2 100644
--- a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
+++ b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
@@ -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 @@ abstract class ViewsFormBase extends FormBase implements ViewsFormInterface {
// 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
diff --git a/core/modules/views_ui/src/Tests/RowUITest.php b/core/modules/views_ui/src/Tests/RowUITest.php
index 08326a5..67e415d 100644
--- a/core/modules/views_ui/src/Tests/RowUITest.php
+++ b/core/modules/views_ui/src/Tests/RowUITest.php
@@ -61,7 +61,7 @@ class RowUITest extends UITestBase {
// 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.');
}
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 64f8cae..588f379 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -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 @@ class ViewUI implements ViewStorageInterface {
// 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();
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index bbabd44..b680217 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -205,7 +205,7 @@ class FormBuilderTest extends FormTestBase {
$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 @@ class FormBuilderTest extends FormTestBase {
$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 @@ class FormBuilderTest extends FormTestBase {
$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 @@ class FormBuilderTest extends FormTestBase {
$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 @@ class FormBuilderTest extends FormTestBase {
$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 @@ class FormBuilderTest extends FormTestBase {
$form = $this->formBuilder->buildForm($form_arg, $form_state);
$this->assertFormElement($expected_form, $form, 'test');
$this->assertSame($form_id, $form_state['build_info']['form_id']);
- $this->assertSame($form_id, $form['#id']);
+ $this->assertArrayHasKey('#id', $form);
}
/**
@@ -403,13 +403,13 @@ class FormBuilderTest extends FormTestBase {
->setMethods(array('drupalSetMessage'))
->getMock();
$form = $this->simulateFormSubmission($form_id, $form_arg, $form_state);
- $this->assertSame($form_id, $form['#id']);
+ $this->assertSame('test-form-id', $form['#id']);
$form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
->setMethods(array('drupalSetMessage'))
->getMock();
$form = $this->simulateFormSubmission($form_id, $form_arg, $form_state);
- $this->assertSame("$form_id--2", $form['#id']);
+ $this->assertSame('test-form-id--2', $form['#id']);
}
}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index 3bb9f4a..f3a9443 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -7,6 +7,7 @@
namespace Drupal\Tests\Core\Form {
+use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBuilder;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
@@ -168,7 +169,7 @@ abstract class FormTestBase extends UnitTestCase {
* {@inheritdoc}
*/
protected function tearDown() {
- $this->formBuilder->drupalStaticReset();
+ Html::resetSeenIds();
}
/**
diff --git a/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php b/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php
new file mode 100644
index 0000000..cd78d20
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Utility/HtmlTest.php
@@ -0,0 +1,213 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Utility\HtmlTest.
+ */
+
+namespace Drupal\Tests\Core\Utility;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests \Drupal\Component\Utility\Html.
+ *
+ * @group Common
+ *
+ * @coversDefaultClass \Drupal\Component\Utility\Html
+ */
+class HtmlTest extends UnitTestCase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ $property = new \ReflectionProperty('Drupal\Component\Utility\Html', 'seenIdsInit');
+ $property->setAccessible(TRUE);
+ $property->setValue(NULL);
+ }
+
+ /**
+ * Tests the Html::cleanCssIdentifier() method.
+ *
+ * @param string $expected
+ * The expected result.
+ * @param string $source
+ * The string being transformed to an ID.
+ * @param array|null $filter
+ * (optional) An array of string replacements to use on the identifier. If
+ * NULL, no filter will be passed and a default will be used.
+ *
+ * @dataProvider providerTestCleanCssIdentifier
+ *
+ * @covers ::cleanCssIdentifier
+ */
+ public function testCleanCssIdentifier($expected, $source, $filter = NULL) {
+ if ($filter !== NULL) {
+ $this->assertSame($expected, Html::cleanCssIdentifier($source, $filter));
+ }
+ else {
+ $this->assertSame($expected, Html::cleanCssIdentifier($source));
+ }
+ }
+
+ /**
+ * Provides test data for testCleanCssIdentifier().
+ *
+ * @return array
+ * Test data.
+ */
+ public function providerTestCleanCssIdentifier() {
+ $id1 = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
+ $id2 = '¡¢£¤¥';
+ $id3 = 'css__identifier__with__double__underscores';
+ return array(
+ // Verify that no valid ASCII characters are stripped from the identifier.
+ array($id1, $id1, array()),
+ // Verify that valid UTF-8 characters are not stripped from the identifier.
+ array($id2, $id2, array()),
+ // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
+ array($id3, $id3),
+ // Verify that double underscores are not stripped from the identifier.
+ array('invalididentifier', 'invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()),
+ // Verify that an identifier starting with a digit is replaced.
+ array('_cssidentifier', '1cssidentifier', array()),
+ // Verify that an identifier starting with a hyphen followed by a digit is
+ // replaced.
+ array('__cssidentifier', '-1cssidentifier', array()),
+ // Verify that an identifier starting with two hyphens is replaced.
+ array('__cssidentifier', '--cssidentifier', array())
+ );
+ }
+
+ /**
+ * Tests that Html::getClass() cleans the class name properly.
+ *
+ * @coversDefaultClass ::getClass
+ */
+ public function testHtmlClass() {
+ // Verify Drupal coding standards are enforced.
+ $this->assertSame(Html::getClass('CLASS NAME_[Ü]'), 'class-name--ü', 'Enforce Drupal coding standards.');
+ }
+
+ /**
+ * Tests the Html::getUniqueId() method.
+ *
+ * @param string $expected
+ * The expected result.
+ * @param string $source
+ * The string being transformed to an ID.
+ * @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.
+ * @param bool $reset
+ * (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE.
+ *
+ * @dataProvider providerTestHtmlGetUniqueIdWithAjaxIds
+ *
+ * @covers ::getUniqueId
+ */
+ public function testHtmlGetUniqueIdWithAjaxIds($expected, $source, $reset = FALSE) {
+ if ($reset) {
+ Html::resetSeenIds();
+ }
+ Html::setAjaxHtmlIds('test-unique-id1 test-unique-id2--3');
+ $this->assertSame($expected, Html::getUniqueId($source));
+ }
+
+ /**
+ * Provides test data for testHtmlGetId().
+ *
+ * @return array
+ * Test data.
+ */
+ public function providerTestHtmlGetUniqueIdWithAjaxIds() {
+ return array(
+ array('test-unique-id1--2', 'test-unique-id1', TRUE),
+ array('test-unique-id1--3', 'test-unique-id1'),
+ array('test-unique-id2--4', 'test-unique-id2', TRUE),
+ array('test-unique-id2--5', 'test-unique-id2'),
+ );
+ }
+
+ /**
+ * 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) {
+ $this->assertSame($expected, Html::getId($source));
+ }
+
+ /**
+ * Provides test data for testHtmlGetId().
+ *
+ * @return array
+ * Test data.
+ */
+ public function providerTestHtmlGetId() {
+ $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'),
+ array('test-unique-id', 'test-unique-id'),
+ );
+ }
+
+}