diff --git a/core/includes/common.inc b/core/includes/common.inc index 17f83ad63656ed349250af6f9e98d3a901003d0a..9f50506e945bb98fe688fc9a5806913c25ca958e 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -6934,6 +6934,9 @@ function drupal_common_theme() { 'tel' => array( 'render element' => 'element', ), + 'email' => array( + 'render element' => 'element', + ), 'form' => array( 'render element' => 'element', ), diff --git a/core/includes/form.inc b/core/includes/form.inc index d726f5c18563ee0cfe33e68721f63c8fdeac5268..9051eef2a3a99f0871cb74f23049aeeae557373d 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -3749,6 +3749,56 @@ function theme_textfield($variables) { return $output . $extra; } +/** + * Returns HTML for an email form element. + * + * @param $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + * Properties used: #title, #value, #description, #size, #maxlength, + * #placeholder, #required, #attributes, #autocomplete_path. + * + * @ingroup themeable + */ +function theme_email($variables) { + $element = $variables['element']; + $element['#attributes']['type'] = 'email'; + element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder')); + _form_set_class($element, array('form-email')); + + $extra = ''; + if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) { + drupal_add_library('system', 'drupal.autocomplete'); + $element['#attributes']['class'][] = 'form-autocomplete'; + + $attributes = array(); + $attributes['type'] = 'hidden'; + $attributes['id'] = $element['#attributes']['id'] . '-autocomplete'; + $attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE)); + $attributes['disabled'] = 'disabled'; + $attributes['class'][] = 'autocomplete'; + $extra = '<input' . drupal_attributes($attributes) . ' />'; + } + + $output = '<input' . drupal_attributes($element['#attributes']) . ' />'; + + return $output . $extra; +} + +/** + * Form element validation handler for #type 'email'. + * + * Note that #maxlength and #required is validated by _form_validate() already. + */ +function form_validate_email(&$element, &$form_state) { + $value = trim($element['#value']); + form_set_value($element, $value, $form_state); + + if ($value !== '' && !valid_email_address($value)) { + form_error($element, t('The e-mail address %mail is not valid.', array('%mail' => $value))); + } +} + /** * Returns HTML for a tel form element. * diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index f16e094d8e6bf7c57301b82c8c7136a5978ebe84..49147a807826338251dd94f59c65e65446a67f83 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1747,7 +1747,7 @@ function _install_configure_form($form, &$form_state, &$install_state) { '#weight' => -20, ); $form['site_information']['site_mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => st('Site e-mail address'), '#default_value' => ini_get('sendmail_from'), '#description' => st("Automated e-mails, such as registration information, will be sent from this address. Use an address ending in your site's domain to help prevent these e-mails from being flagged as spam."), @@ -1770,9 +1770,9 @@ function _install_configure_form($form, &$form_state, &$install_state) { '#attributes' => array('class' => array('username')), ); - $form['admin_account']['account']['mail'] = array('#type' => 'textfield', + $form['admin_account']['account']['mail'] = array( + '#type' => 'email', '#title' => st('E-mail address'), - '#maxlength' => EMAIL_MAX_LENGTH, '#required' => TRUE, '#weight' => -5, ); @@ -1849,12 +1849,6 @@ function install_configure_form_validate($form, &$form_state) { if ($error = user_validate_name($form_state['values']['account']['name'])) { form_error($form['admin_account']['account']['name'], $error); } - if ($error = user_validate_mail($form_state['values']['account']['mail'])) { - form_error($form['admin_account']['account']['mail'], $error); - } - if ($error = user_validate_mail($form_state['values']['site_mail'])) { - form_error($form['site_information']['site_mail'], $error); - } } /** diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 664acf3674220390b7ee2374e05b80defafd1940..946583669cebb3d6542f66db28cb62f9028b9d07 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1781,7 +1781,7 @@ function comment_form($form, &$form_state, $comment) { // Add author e-mail and homepage fields depending on the current user. $form['author']['mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => t('E-mail'), '#default_value' => $comment->mail, '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT), @@ -1982,9 +1982,6 @@ function comment_form_validate($form, &$form_state) { } } } - if ($form_state['values']['mail'] && !valid_email_address($form_state['values']['mail'])) { - form_set_error('mail', t('The e-mail address you specified is not valid.')); - } if ($form_state['values']['homepage'] && !valid_url($form_state['values']['homepage'], TRUE)) { form_set_error('homepage', t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.')); } diff --git a/core/modules/contact/contact.pages.inc b/core/modules/contact/contact.pages.inc index 289e38b7a2a8b233a3d21c89e245ed49e535bf3e..bf096a7b8ee438b9a171b3b513aaee4e0dd48abb 100644 --- a/core/modules/contact/contact.pages.inc +++ b/core/modules/contact/contact.pages.inc @@ -71,9 +71,8 @@ function contact_site_form($form, &$form_state) { '#required' => TRUE, ); $form['mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => t('Your e-mail address'), - '#maxlength' => 255, '#default_value' => $user->uid ? $user->mail : '', '#required' => TRUE, ); @@ -121,9 +120,6 @@ function contact_site_form_validate($form, &$form_state) { if (!$form_state['values']['cid']) { form_set_error('cid', t('You must select a valid category.')); } - if (!valid_email_address($form_state['values']['mail'])) { - form_set_error('mail', t('You must enter a valid e-mail address.')); - } } /** @@ -211,9 +207,8 @@ function contact_personal_form($form, &$form_state, $recipient) { '#required' => TRUE, ); $form['mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => t('Your e-mail address'), - '#maxlength' => 255, '#default_value' => $user->uid ? $user->mail : '', '#required' => TRUE, ); @@ -249,17 +244,6 @@ function contact_personal_form($form, &$form_state, $recipient) { return $form; } -/** - * Form validation handler for contact_personal_form(). - * - * @see contact_personal_form_submit() - */ -function contact_personal_form_validate($form, &$form_state) { - if (!valid_email_address($form_state['values']['mail'])) { - form_set_error('mail', t('You must enter a valid e-mail address.')); - } -} - /** * Form submission handler for contact_personal_form(). * diff --git a/core/modules/contact/contact.test b/core/modules/contact/contact.test index 48c8bb01fd63a0c0172adabf0c368c252ae235d3..d7f26acf17d1ab75f73132f195cce50765d7304d 100644 --- a/core/modules/contact/contact.test +++ b/core/modules/contact/contact.test @@ -126,7 +126,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase { $this->assertText(t('Your e-mail address field is required.'), t('E-mail required.')); $this->submitContact($this->randomName(16), $invalid_recipients[0], $this->randomName(16), $categories[0], $this->randomName(64)); - $this->assertText(t('You must enter a valid e-mail address.'), t('Valid e-mail required.')); + $this->assertRaw(t('The e-mail address %mail is not valid.', array('%mail' => 'invalid')), 'Valid e-mail required.'); $this->submitContact($this->randomName(16), $recipients[0], '', $categories[0], $this->randomName(64)); $this->assertText(t('Subject field is required.'), t('Subject required.')); diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php index e5d36c338ba7809dbc12e60566402df657aa41fb..940915fde215af011f4e66dd21281fd6b64201ad 100644 --- a/core/modules/simpletest/drupal_web_test_case.php +++ b/core/modules/simpletest/drupal_web_test_case.php @@ -2250,6 +2250,7 @@ protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { case 'textarea': case 'hidden': case 'password': + case 'email': $post[$name] = $edit[$name]; unset($edit[$name]); break; diff --git a/core/modules/simpletest/tests/form.test b/core/modules/simpletest/tests/form.test index 6e40f1f9fc6165beaa6a3f3fd706e64ccae8f666..4eefff462c9daaef35fa20910fb01a37ef2e153a 100644 --- a/core/modules/simpletest/tests/form.test +++ b/core/modules/simpletest/tests/form.test @@ -1627,3 +1627,47 @@ class FormCheckboxTestCase extends DrupalWebTestCase { } } } + +/** + * Tests email element. + */ +class FormEmailTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + + public static function getInfo() { + return array( + 'name' => 'Form API email', + 'description' => 'Tests the form API email element.', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Tests that #type 'email' fields are properly validated. + */ + function testFormEmail() { + $edit = array(); + $edit['email'] = 'invalid'; + $edit['email_required'] = ' '; + $this->drupalPost('form-test/email', $edit, 'Submit'); + $this->assertRaw(t('The e-mail address %mail is not valid.', array('%mail' => 'invalid'))); + $this->assertRaw(t('!name field is required.', array('!name' => 'Address'))); + + $edit = array(); + $edit['email_required'] = ' foo.bar@example.com '; + $values = drupal_json_decode($this->drupalPost('form-test/email', $edit, 'Submit')); + $this->assertIdentical($values['email'], ''); + $this->assertEqual($values['email_required'], 'foo.bar@example.com'); + + $edit = array(); + $edit['email'] = 'foo@example.com'; + $edit['email_required'] = 'example@drupal.org'; + $values = drupal_json_decode($this->drupalPost('form-test/email', $edit, 'Submit')); + $this->assertEqual($values['email'], 'foo@example.com'); + $this->assertEqual($values['email_required'], 'example@drupal.org'); + } +} diff --git a/core/modules/simpletest/tests/form_test.module b/core/modules/simpletest/tests/form_test.module index 76e5c260de3d62ce449ff21140cedb44c2c38acf..a64fb62e278638718ce8612db0fe749dfbf9036a 100644 --- a/core/modules/simpletest/tests/form_test.module +++ b/core/modules/simpletest/tests/form_test.module @@ -125,6 +125,12 @@ function form_test_menu() { 'page arguments' => array('form_test_checkboxes_radios'), 'access callback' => TRUE, ); + $items['form-test/email'] = array( + 'title' => 'E-Mail fields', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_email'), + 'access callback' => TRUE, + ); $items['form-test/disabled-elements'] = array( 'title' => t('Form test'), @@ -1097,6 +1103,39 @@ function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) { return $form; } +/** + * Form constructor for testing #type 'email' elements. + * + * @see form_test_email_submit() + * @ingroup forms + */ +function form_test_email($form, &$form_state) { + $form['email'] = array( + '#type' => 'email', + '#title' => 'E-Mail address', + '#description' => 'An e-mail address.', + ); + $form['email_required'] = array( + '#type' => 'email', + '#title' => 'Address', + '#required' => TRUE, + '#description' => 'A required e-mail address field.', + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Submit', + ); + return $form; +} + +/** + * Form submission handler for form_test_email(). + */ +function form_test_email_submit($form, &$form_state) { + drupal_json_output($form_state['values']); + exit(); +} + /** * Build a form to test disabled elements. */ diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index d28ecfbe207b1052351d6c9bb59c0f3c14a465de..1c865a6356f25deee43107fcc37eb92385dcbf0e 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -1477,7 +1477,7 @@ function system_site_information_settings() { '#description' => t("How this is used depends on your site's theme."), ); $form['site_information']['site_mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => t('E-mail address'), '#default_value' => variable_get('site_mail', ini_get('sendmail_from')), '#description' => t("The <em>From</em> address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"), @@ -1532,10 +1532,6 @@ function system_site_information_settings() { * Validates the submitted site-information form. */ function system_site_information_settings_validate($form, &$form_state) { - // Validate the e-mail address. - if ($error = user_validate_mail($form_state['values']['site_mail'])) { - form_set_error('site_mail', $error); - } // Check for empty front page path. if (empty($form_state['values']['site_frontpage'])) { // Set to default "user". diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 92092d293d9589222f6ba7274275f580ed0c17ab..1c0543d158c9df0c577cbb29ad8b95728cf79261 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -374,6 +374,16 @@ function system_element_info() { '#theme' => 'tel', '#theme_wrappers' => array('form_element'), ); + $types['email'] = array( + '#input' => TRUE, + '#size' => 60, + '#maxlength' => EMAIL_MAX_LENGTH, + '#autocomplete_path' => FALSE, + '#process' => array('ajax_process_form'), + '#element_validate' => array('form_validate_email'), + '#theme' => 'email', + '#theme_wrappers' => array('form_element'), + ); $types['machine_name'] = array( '#input' => TRUE, '#default_value' => NULL, diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 3ac4cd682313d0f4a76bdaf4ab163dddeb4ded60..7fb442f15d070e64da0f86b758ec19eeb541fad4 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -395,9 +395,6 @@ function user_save($account, $edit = array()) { // Avoid overwriting an existing password with a blank password. unset($edit['pass']); } - if (isset($edit['mail'])) { - $edit['mail'] = trim($edit['mail']); - } // Load the stored entity, if any. if (!empty($account->uid) && !isset($account->original)) { @@ -608,28 +605,6 @@ function user_validate_name($name) { } } -/** - * Validates a user's email address. - * - * Checks that a user's email address exists and follows all standard - * validation rules. Returns error messages when the address is invalid. - * - * @param $mail - * A user's email address. - * - * @return - * If the address is invalid, a human-readable error message is returned. - * If the address is valid, nothing is returned. - */ -function user_validate_mail($mail) { - if (!$mail) { - return t('You must enter an e-mail address.'); - } - if (!valid_email_address($mail)) { - return t('The e-mail address %mail is not valid.', array('%mail' => $mail)); - } -} - /** * Validates an image uploaded by a user. * @@ -965,9 +940,8 @@ function user_account_form(&$form, &$form_state) { ); $form['account']['mail'] = array( - '#type' => 'textfield', + '#type' => 'email', '#title' => t('E-mail address'), - '#maxlength' => EMAIL_MAX_LENGTH, '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'), '#required' => TRUE, '#default_value' => (!$register ? $account->mail : ''), @@ -1149,22 +1123,15 @@ function user_account_form_validate($form, &$form_state) { } } - // Trim whitespace from mail, to prevent confusing 'e-mail not valid' - // warnings often caused by cutting and pasting. - $mail = trim($form_state['values']['mail']); - form_set_value($form['account']['mail'], $mail, $form_state); + $mail = $form_state['values']['mail']; - // Validate the e-mail address, and check if it is taken by an existing user. - if ($error = user_validate_mail($form_state['values']['mail'])) { - form_set_error('mail', $error); - } - elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($form_state['values']['mail']), 'LIKE')->range(0, 1)->execute()->fetchField()) { + if ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($mail), 'LIKE')->range(0, 1)->execute()->fetchField()) { // Format error message dependent on whether the user is logged in or not. if ($GLOBALS['user']->uid) { - form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail']))); + form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail))); } else { - form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password')))); + form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $mail, '@password' => url('user/password')))); } } diff --git a/core/modules/user/user.test b/core/modules/user/user.test index 0c5f90ffc908e54b011049cd138cc0a5a7e1c3ea..5b97e8b3e1ae87f8c83074d1fad29dd0bafbfb5d 100644 --- a/core/modules/user/user.test +++ b/core/modules/user/user.test @@ -292,20 +292,6 @@ class UserValidationTestCase extends DrupalWebTestCase { $this->$test($result, $description . ' (' . $name . ')'); } } - - // Mail validation. More extensive tests can be found at common.test - function testMailAddresses() { - $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'), - '' => array('Empty mail address', 'assertNotNull'), - 'foo' => array('Invalid mail address', 'assertNotNull'), - 'foo@example.com' => array('Valid mail address', 'assertNull'), - ); - foreach ($test_cases as $name => $test_case) { - list($description, $test) = $test_case; - $result = user_validate_mail($name); - $this->$test($result, $description . ' (' . $name . ')'); - } - } } /** diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css index 7a743b1fb001e8b05cc31fddb66e1525b388b33d..739ae81ae46e2908d1314b571ba5a4a0a7b68689 100644 --- a/core/themes/bartik/css/style.css +++ b/core/themes/bartik/css/style.css @@ -1195,6 +1195,7 @@ select.form-select { } input.form-text, input.form-tel, +input.form-email, textarea.form-textarea, select.form-select { border: 1px solid #ccc; diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css index 66914f6fb1036a278006b51eb373ad136c4eabe9..4214453410c0fd7a92c114a3fc413ec084017286 100644 --- a/core/themes/seven/style.css +++ b/core/themes/seven/style.css @@ -602,6 +602,7 @@ div.teaser-checkbox .form-item, .form-disabled input.form-autocomplete, .form-disabled input.form-text, .form-disabled input.form-tel, +.form-disabled input.form-email, .form-disabled input.form-file, .form-disabled textarea.form-textarea, .form-disabled select.form-select { @@ -689,6 +690,7 @@ input.form-button-disabled:active { input.form-autocomplete, input.form-text, input.form-tel, +input.form-email, input.form-file, textarea.form-textarea, select.form-select { @@ -703,6 +705,7 @@ select.form-select { } input.form-text:focus, input.form-tel:focus, +input.form-email:focus, input.form-file:focus, textarea.form-textarea:focus, select.form-select:focus {