diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/common.inc | 73 | ||||
-rw-r--r-- | includes/form.inc | 52 | ||||
-rw-r--r-- | includes/theme.inc | 14 |
3 files changed, 87 insertions, 52 deletions
diff --git a/includes/common.inc b/includes/common.inc index f2db46b28..878e8106d 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -3095,6 +3095,79 @@ function drupal_clear_css_cache() { } /** + * Prepare a string for use as a valid CSS identifier (element, class or ID selector). + * + * 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 $identifier + * The CSS identifier to clean. + * @param $filter + * An array of string replacements to use on the identifier. + * @return + * The cleaned identifier. + */ +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); + + return $identifier; +} + +/** + * Prepare a string for use as a valid CSS 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 $class + * The class name to clean. + * @return + * The cleaned class name. + */ +function drupal_css_class($class) { + return drupal_clean_css_identifier(drupal_strtolower($class)); +} + +/** + * Prepare a string for use as a valid HTML ID and guarantee uniqueness. + * + * @param $id + * The ID to clean. + * @return + * The cleaned ID. + */ +function drupal_css_id($id) { + $seen_ids = &drupal_static(__FUNCTION__, array()); + $id = drupal_clean_css_identifier(drupal_strtolower($id)); + + // Ensure IDs are unique. The first occurrence is held but left alone. + // Subsequent occurrences get a number appended to them. This incrementing + // will almost certainly break code that relies on explicit HTML IDs in forms + // that appear more than once on the page, but the alternative is outputting + // duplicate IDs, which would break JS code and XHTML validity anyways. For + // now, it's an acceptable stopgap solution. + if (isset($seen_ids[$id])) { + $id = $id . '-' . ++$seen_ids[$id]; + } + else { + $seen_ids[$id] = 1; + } + + return $id; +} + +/** * Add a JavaScript file, setting or inline code to the page. * * The behavior of this function depends on the parameters it is called with. diff --git a/includes/form.inc b/includes/form.inc index 3ee8af7b7..82d84b4f8 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -547,12 +547,12 @@ function drupal_process_form($form_id, &$form, &$form_state) { if ($form_state['process_input']) { drupal_validate_form($form_id, $form, $form_state); - // form_clean_id() maintains a cache of element IDs it has seen, + // drupal_css_id() maintains a cache of element IDs it has seen, // so it can prevent duplicates. We want to be sure we reset that // cache when a form is processed, so scenarios that result in // the form being built behind the scenes and again for the // browser don't increment all the element IDs needlessly. - drupal_static_reset('form_clean_id'); + drupal_static_reset('drupal_css_id'); if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) { // Execute form submit handlers. @@ -636,7 +636,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { elseif (isset($user->uid) && $user->uid && !$form_state['programmed']) { $form['#token'] = $form_id; $form['form_token'] = array( - '#id' => form_clean_id('edit-' . $form_id . '-form-token'), + '#id' => drupal_css_id('edit-' . $form_id . '-form-token'), '#type' => 'token', '#default_value' => drupal_get_token($form['#token']), ); @@ -646,11 +646,11 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { $form['form_id'] = array( '#type' => 'hidden', '#value' => $form_id, - '#id' => form_clean_id("edit-$form_id"), + '#id' => drupal_css_id("edit-$form_id"), ); } if (!isset($form['#id'])) { - $form['#id'] = form_clean_id($form_id); + $form['#id'] = drupal_css_id($form_id); } $form += element_info('form'); @@ -1046,7 +1046,7 @@ function form_builder($form_id, $element, &$form_state) { } if (!isset($element['#id'])) { - $element['#id'] = form_clean_id('edit-' . implode('-', $element['#parents'])); + $element['#id'] = drupal_css_id('edit-' . implode('-', $element['#parents'])); } // Handle input elements. if (!empty($element['#input'])) { @@ -1919,7 +1919,7 @@ function form_process_radios($element) { '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL, '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], - '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)), + '#id' => drupal_css_id('edit-' . implode('-', $parents_for_id)), '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); } @@ -2235,7 +2235,7 @@ function form_process_tableselect($element) { '#default_value' => ($element['#default_value'] == $key) ? $key : NULL, '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], - '#id' => form_clean_id('edit-' . implode('-', $parents_for_id)), + '#id' => drupal_css_id('edit-' . implode('-', $parents_for_id)), '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); } @@ -2684,42 +2684,6 @@ function _form_set_class(&$element, $class = array()) { } /** - * Prepare an HTML ID attribute string for a form item. - * - * Remove invalid characters and guarantee uniqueness. - * - * @param $id - * The ID to clean. - * @param $flush - * If set to TRUE, the function will flush and reset the static array - * which is built to test the uniqueness of element IDs. This is only - * used if a form has completed the validation process. This parameter - * should never be set to TRUE if this function is being called to - * assign an ID to the #ID element. - * @return - * The cleaned ID. - */ -function form_clean_id($id = NULL) { - $seen_ids = &drupal_static(__FUNCTION__, array()); - $id = str_replace(array('][', '_', ' '), '-', $id); - - // Ensure IDs are unique. The first occurrence is held but left alone. - // Subsequent occurrences get a number appended to them. This incrementing - // will almost certainly break code that relies on explicit HTML IDs in - // forms that appear more than once on the page, but the alternative is - // outputting duplicate IDs, which would break JS code and XHTML - // validity anyways. For now, it's an acceptable stopgap solution. - if (isset($seen_ids[$id])) { - $id = $id . '-' . $seen_ids[$id]++; - } - else { - $seen_ids[$id] = 1; - } - - return $id; -} - -/** * @} End of "defgroup form_api". */ diff --git a/includes/theme.inc b/includes/theme.inc index 71fbf9ba7..ca0e533aa 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -2128,18 +2128,17 @@ function template_preprocess_html(&$variables) { if ($suggestions = template_page_suggestions(arg(), 'page')) { foreach ($suggestions as $suggestion) { if ($suggestion != 'page-front') { - // Add current suggestion to page classes to make it possible to theme the page - // depending on the current page type (e.g. node, admin, user, etc.) as well as - // more specific data like node-12 or node-edit. To avoid illegal characters in - // the class, we're removing everything disallowed. We are not using 'a-z' as - // that might leave in certain international characters (e.g. German umlauts). - $variables['classes_array'][] = preg_replace('![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s', '', form_clean_id(drupal_strtolower($suggestion))); + // Add current suggestion to page classes to make it possible to theme + // the page depending on the current page type (e.g. node, admin, user, + // etc.) as well as more specific data like node-12 or node-edit. + $variables['classes_array'][] = drupal_css_class($suggestion); } } } + // If on an individual node page, add the node type to body classes. if ($node = menu_get_object()) { - $variables['classes_array'][] = 'node-type-' . form_clean_id($node->type); + $variables['classes_array'][] = drupal_css_class('node-type-' . $node->type); } // RDFa allows annotation of XHTML pages with RDF data, while GRDDL provides @@ -2150,7 +2149,6 @@ function template_preprocess_html(&$variables) { $variables['language'] = $GLOBALS['language']; $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr'; - // Add favicon. if (theme_get_setting('toggle_favicon')) { $favicon = theme_get_setting('favicon'); |