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: ...@@ -564,8 +564,8 @@ services:
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
arguments: ['@settings'] arguments: ['@settings']
ajax_response_subscriber: ajax_subscriber:
class: Drupal\Core\EventSubscriber\AjaxResponseSubscriber class: Drupal\Core\EventSubscriber\AjaxSubscriber
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }
route_enhancer.param_conversion: route_enhancer.param_conversion:
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\Bytes; use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Number; use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SortArray; use Drupal\Component\Utility\SortArray;
...@@ -1299,25 +1300,12 @@ function drupal_clear_css_cache() { ...@@ -1299,25 +1300,12 @@ function drupal_clear_css_cache() {
* *
* @return * @return
* The cleaned identifier. * 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(' ' => '-', '_' => '-', '__' => '__', '/' => '-', '[' => '-', ']' => '')) { function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '__' => '__', '/' => '-', '[' => '-', ']' => '')) {
// By default, we filter using Drupal's coding standards. return Html::cleanCssIdentifier($identifier, $filter);
$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;
} }
/** /**
...@@ -1331,16 +1319,12 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_ ...@@ -1331,16 +1319,12 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
* *
* @return * @return
* The cleaned class name. * 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) { function drupal_html_class($class) {
// The output of this function will never change, so this uses a normal return Html::getClass($class);
// static instead of drupal_static().
static $classes = array();
if (!isset($classes[$class])) {
$classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
}
return $classes[$class];
} }
/** /**
...@@ -1369,66 +1353,12 @@ function drupal_html_class($class) { ...@@ -1369,66 +1353,12 @@ function drupal_html_class($class) {
* *
* @return * @return
* The cleaned ID. * 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) { function drupal_html_id($id) {
// If this is an Ajax request, then content returned by this page request will return Html::getUniqueId($id);
// 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;
} }
/** /**
...@@ -1444,21 +1374,12 @@ function drupal_html_id($id) { ...@@ -1444,21 +1374,12 @@ function drupal_html_id($id) {
* The cleaned ID. * The cleaned ID.
* *
* @see drupal_html_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) { function drupal_clean_id_identifier($id) {
$id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => '')); return Html::getId($id);
// 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;
} }
/** /**
......
...@@ -14,6 +14,229 @@ ...@@ -14,6 +14,229 @@
*/ */
class Html { 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. * Normalizes an HTML snippet.
* *
......
...@@ -2,18 +2,32 @@ ...@@ -2,18 +2,32 @@
/** /**
* @file * @file
* Contains \Drupal\Core\EventSubscriber\AjaxResponseSubscriber. * Contains \Drupal\Core\EventSubscriber\AjaxSubscriber.
*/ */
namespace Drupal\Core\EventSubscriber; namespace Drupal\Core\EventSubscriber;
use Drupal\Component\Utility\Html;
use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\AjaxResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents; 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. * Renders the ajax commands right before preparing the result.
...@@ -33,6 +47,7 @@ public function onResponse(FilterResponseEvent $event) { ...@@ -33,6 +47,7 @@ public function onResponse(FilterResponseEvent $event) {
*/ */
public static function getSubscribedEvents() { public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = array('onResponse', -100); $events[KernelEvents::RESPONSE][] = array('onResponse', -100);
$events[KernelEvents::REQUEST][] = array('onREquest', 50);
return $events; return $events;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\Core\Form; namespace Drupal\Core\Form;
use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String; use Drupal\Component\Utility\String;
use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\UrlHelper;
...@@ -384,10 +385,10 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) { ...@@ -384,10 +385,10 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
// Assign a default CSS class name based on $form_id. // Assign a default CSS class name based on $form_id.
// This happens here and not in self::prepareForm() in order to allow the // This happens here and not in self::prepareForm() in order to allow the
// form constructor function to override or remove the default class. // 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. // Same for the base form ID, if any.
if (isset($form_state['build_info']['base_form_id'])) { 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, // 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) { ...@@ -459,7 +460,7 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
// element IDs needlessly. // element IDs needlessly.
if (!FormState::hasAnyErrors()) { if (!FormState::hasAnyErrors()) {
// In case of errors, do not break HTML IDs of other forms. // 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()) { if (!$form_state['rebuild'] && !FormState::hasAnyErrors()) {
...@@ -561,7 +562,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { ...@@ -561,7 +562,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
else { else {
$form['#token'] = $form_id; $form['#token'] = $form_id;
$form['form_token'] = array( $form['form_token'] = array(
'#id' => $this->drupalHtmlId('edit-' . $form_id . '-form-token'), '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
'#type' => 'token', '#type' => 'token',
'#default_value' => $this->csrfToken->get($form['#token']), '#default_value' => $this->csrfToken->get($form['#token']),
// Form processing and validation requires this value, so ensure the // Form processing and validation requires this value, so ensure the
...@@ -576,7 +577,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { ...@@ -576,7 +577,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
$form['form_id'] = array( $form['form_id'] = array(
'#type' => 'hidden', '#type' => 'hidden',
'#value' => $form_id, '#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 // Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree // submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere. // and #parents being set elsewhere.
...@@ -584,7 +585,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { ...@@ -584,7 +585,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
); );
} }
if (!isset($form['#id'])) { if (!isset($form['#id'])) {
$form['#id'] = $this->drupalHtmlId($form_id); $form['#id'] = Html::getUniqueId($form_id);
} }
$form += $this->getElementInfo('form'); $form += $this->getElementInfo('form');
...@@ -702,7 +703,7 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state ...@@ -702,7 +703,7 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
} }
if (!isset($element['#id'])) { 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 // Add the aria-describedby attribute to associate the form control with its
...@@ -1084,31 +1085,6 @@ protected function getElementInfo($type) { ...@@ -1084,31 +1085,6 @@ protected function getElementInfo($type) {
return element_info($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. * Gets the current active user.
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\block\Tests; namespace Drupal\block\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\UrlCacheContext; use Drupal\Core\Cache\UrlCacheContext;
use Drupal\simpletest\DrupalUnitTestBase; use Drupal\simpletest\DrupalUnitTestBase;
...@@ -94,7 +95,7 @@ public function testBasicRendering() { ...@@ -94,7 +95,7 @@ public function testBasicRendering() {
$this->assertEqual(drupal_render($output), $expected_output); $this->assertEqual(drupal_render($output), $expected_output);
// Reset the HTML IDs so that the next render is not affected. // 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. // Test the rendering of a block with a given title.
$entity = $this->controller->create(array( $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.');
}