Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.932 diff -u -p -r1.932 common.inc --- includes/common.inc 10 Jul 2009 05:45:56 -0000 1.932 +++ includes/common.inc 12 Jul 2009 23:30:18 -0000 @@ -2122,19 +2122,23 @@ function url($path = NULL, array $option /** * Format an attribute string to insert in a tag. * + * Each array key and its value will be formatted into an HTML attribute string. + * If a value is itself an array, then each array element is concatenated with a + * space between each value (e.g. a multi-value class attribute). + * * @param $attributes * An associative array of HTML attributes. * @return * An HTML string ready for insertion in a tag. */ function drupal_attributes($attributes = array()) { - if (is_array($attributes)) { - $t = ''; - foreach ($attributes as $key => $value) { - $t .= " $key=" . '"' . check_plain($value) . '"'; + foreach ($attributes as $attribute => $data) { + if (is_array($data)) { + $data = implode(' ', $data); } - return $t; + $items[] = $attribute . '="' . check_plain($data) . '"'; } + return isset($items) ? ' ' . implode(' ', $items) : ''; } /** @@ -4100,7 +4104,7 @@ function drupal_common_theme() { 'arguments' => array('links' => NULL, 'attributes' => array('class' => 'links')), ), 'image' => array( - 'arguments' => array('path' => NULL, 'alt' => '', 'title' => '', 'attributes' => NULL, 'getsize' => TRUE), + 'arguments' => array('path' => NULL, 'alt' => '', 'title' => '', 'attributes' => array(), 'getsize' => TRUE), ), 'breadcrumb' => array( 'arguments' => array('breadcrumb' => NULL), @@ -4124,7 +4128,7 @@ function drupal_common_theme() { 'arguments' => array('type' => MARK_NEW), ), 'item_list' => array( - 'arguments' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => NULL), + 'arguments' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => array()), ), 'more_help_link' => array( 'arguments' => array('url' => NULL), Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.497 diff -u -p -r1.497 theme.inc --- includes/theme.inc 2 Jul 2009 04:27:22 -0000 1.497 +++ includes/theme.inc 12 Jul 2009 23:30:19 -0000 @@ -1335,7 +1335,7 @@ function theme_links($links, $attributes * @return * A string containing the image tag. */ -function theme_image($path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) { +function theme_image($path, $alt = '', $title = '', $attributes = array(), $getsize = TRUE) { if (!$getsize || (is_file($path) && (list($width, $height, $type, $image_attributes) = @getimagesize($path)))) { $attributes = drupal_attributes($attributes); $url = (url($path) == $path) ? $path : (base_path() . $path); Index: modules/image/image.module =================================================================== RCS file: /cvs/drupal/drupal/modules/image/image.module,v retrieving revision 1.1 diff -u -p -r1.1 image.module --- modules/image/image.module 12 Jul 2009 08:36:34 -0000 1.1 +++ modules/image/image.module 12 Jul 2009 23:30:19 -0000 @@ -29,7 +29,7 @@ function image_menu() { function image_theme() { return array( 'image_style' => array( - 'arguments' => array('style' => NULL, 'path' => NULL, 'alt' => '', 'title' => '', 'attributes' => NULL, 'getsize' => TRUE), + 'arguments' => array('style' => NULL, 'path' => NULL, 'alt' => '', 'title' => '', 'attributes' => array(), 'getsize' => TRUE), ), ); } @@ -666,7 +666,7 @@ function image_effect_apply(&$image, $ef * A string containing the image tag. * @ingroup themeable */ -function theme_image_style($style_name, $path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) { +function theme_image_style($style_name, $path, $alt = '', $title = '', $attributes = array(), $getsize = TRUE) { // theme_image() can only honor the $getsize parameter with local file paths. // The derivative image is not created until it has been requested so the file // may not yet exist, in this case we just fallback to the URL. Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.53 diff -u -p -r1.53 common.test --- modules/simpletest/tests/common.test 10 Jul 2009 05:45:56 -0000 1.53 +++ modules/simpletest/tests/common.test 12 Jul 2009 23:30:19 -0000 @@ -1074,3 +1074,43 @@ class FormatDateUnitTest extends DrupalW drupal_save_session(TRUE); } } + +/** + * Tests for the format_date() function. + */ +class DrupalAttributesUnitTest extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => t('HTML Attributes'), + 'description' => t('Perform unit tests on the drupal_attributes() function.'), + 'group' => t('System') + ); + } + + /** + * Tests that drupal_css_class() cleans the class name properly. + */ + function testDrupalAttributes() { + // Verify that special characters are HTML encoded. + $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&"'<>"', t('HTML encode attribute values.')); + + // Verify multi-value attributes are concatenated with spaces. + $attributes = array('class' => array('first', 'last')); + $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.')); + + // Verify empty attribute values are rendered. + $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.')); + $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.')); + + // Verify multiple attributes are rendered. + $attributes = array( + 'id' => 'id-test', + 'class' => array('first', 'last'), + 'alt' => 'Alternate', + ); + $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.')); + + // Verify empty attributes array is rendered. + $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.')); + } +} Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1011 diff -u -p -r1.1011 user.module --- modules/user/user.module 12 Jul 2009 08:36:35 -0000 1.1011 +++ modules/user/user.module 12 Jul 2009 23:30:19 -0000 @@ -1177,10 +1177,10 @@ function template_preprocess_user_pictur if (isset($filepath)) { $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous')))); if (module_exists('image') && $style = variable_get('user_picture_style', '')) { - $variables['picture'] = theme('image_style', $style, $filepath, $alt, $alt, NULL, FALSE); + $variables['picture'] = theme('image_style', $style, $filepath, $alt, $alt, array(), FALSE); } else { - $variables['picture'] = theme('image', $filepath, $alt, $alt, NULL, FALSE); + $variables['picture'] = theme('image', $filepath, $alt, $alt, array(), FALSE); } if (!empty($account->uid) && user_access('access user profiles')) { $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);