Browse Source

repatched https://www.drupal.org/project/drupal/issues/1754162#comment-9737269

Bachir Soussi Chiadmi 5 years ago
parent
commit
fca4395934
4 changed files with 93 additions and 9 deletions
  1. 31 7
      modules/user/user.module
  2. 2 2
      modules/user/user.module.orig
  3. 53 0
      modules/user/user.test
  4. 7 0
      modules/user/user.test.orig

+ 31 - 7
modules/user/user.module

@@ -2359,14 +2359,26 @@ function user_external_login_register($name, $module) {
  *   following properties:
  *   - uid: The user ID number.
  *   - login: The UNIX timestamp of the user's last login.
+ * @param array $options
+ *   (optional) A keyed array of settings. Supported options are:
+ *   - langcode: A language code to be used when generating locale-sensitive
+ *    urls. If langcode is NULL the users preferred language is used.
  *
  * @return
  *   A unique URL that provides a one-time log in for the user, from which
  *   they can change their password.
  */
-function user_pass_reset_url($account) {
+function user_pass_reset_url($account, $options = array()) {
   $timestamp = REQUEST_TIME;
-  return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE));
+  $url_options = array('absolute' => TRUE);
+  if (isset($options['langcode'])) {
+    $languages = language_list();
+    $url_options['language'] = $languages[$options['langcode']];
+  }
+  else {
+    $url_options['language'] = user_preferred_language($account);
+  }
+  return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), $url_options);
 }
 
 /**
@@ -2378,6 +2390,10 @@ function user_pass_reset_url($account) {
  *   - uid: The user ID number.
  *   - pass: The hashed user password string.
  *   - login: The UNIX timestamp of the user's last login.
+ * @param array $options
+ *   (optional) A keyed array of settings. Supported options are:
+ *   - langcode: A language code to be used when generating locale-sensitive
+ *    urls. If langcode is NULL the users preferred language is used.
  *
  * @return
  *   A unique URL that may be used to confirm the cancellation of the user
@@ -2386,9 +2402,17 @@ function user_pass_reset_url($account) {
  * @see user_mail_tokens()
  * @see user_cancel_confirm()
  */
-function user_cancel_url($account) {
+function user_cancel_url($account, $options = array()) {
   $timestamp = REQUEST_TIME;
-  return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE));
+  $url_options = array('absolute' => TRUE);
+  if (isset($options['langcode'])) {
+    $languages = language_list();
+    $url_options['language'] = $languages[$options['langcode']];
+  }
+  else {
+    $url_options['language'] = user_preferred_language($account);
+  }
+  return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), $url_options);
 }
 
 /**
@@ -2878,7 +2902,7 @@ Your account on [site:name] has been canceled.
   if ($replace) {
     // We do not sanitize the token replacement, since the output of this
     // replacement is intended for an e-mail message, not a web browser.
-    return token_replace($text, $variables, array('language' => $language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+    return token_replace($text, $variables, array('language' => $language, 'langcode' => $langcode, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
   }
 
   return $text;
@@ -2905,8 +2929,8 @@ Your account on [site:name] has been canceled.
  */
 function user_mail_tokens(&$replacements, $data, $options) {
   if (isset($data['user'])) {
-    $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']);
-    $replacements['[user:cancel-url]'] = user_cancel_url($data['user']);
+    $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user'], $options);
+    $replacements['[user:cancel-url]'] = user_cancel_url($data['user'], $options);
   }
 }
 

+ 2 - 2
modules/user/user.module.orig

@@ -637,7 +637,7 @@ function user_validate_name($name) {
   if (strpos($name, '  ') !== FALSE) {
     return t('The username cannot contain multiple spaces in a row.');
   }
-  if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
+  if (preg_match('/[^\x{80}-\x{F7} a-z0-9@+_.\'-]/i', $name)) {
     return t('The username contains an illegal character.');
   }
   if (preg_match('/[\x{80}-\x{A0}' .         // Non-printable ISO-8859-1 + NBSP
@@ -689,7 +689,7 @@ function user_validate_picture(&$form, &$form_state) {
   $validators = array(
     'file_validate_is_image' => array(),
     'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
-    'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
+    'file_validate_size' => array((int) variable_get('user_picture_file_size', '30') * 1024),
   );
 
   // Save the file as a temporary file.

+ 53 - 0
modules/user/user.test

@@ -2321,6 +2321,26 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
     );
   }
 
+  public function setUp() {
+    parent::setUp('locale');
+
+    $account = $this->drupalCreateUser(array('access administration pages', 'administer languages'));
+    $this->drupalLogin($account);
+
+    // Add language.
+    $edit = array('langcode' => 'de');
+    $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+    // Enable URL language detection and selection.
+    $edit = array('language[enabled][locale-url]' => 1);
+    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
+
+    // Reset static caching.
+    drupal_static_reset('language_list');
+    drupal_static_reset('locale_url_outbound_alter');
+    drupal_static_reset('locale_language_url_rewrite_url');
+  }
+
   /**
    * Creates a user, then tests the tokens generated from it.
    */
@@ -2371,6 +2391,39 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
       $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input)));
     }
+
+    $languages = language_list();
+
+    // Generate login and cancel link.
+    $tests = array();
+    $tests['[user:one-time-login-url]'] = user_pass_reset_url($account);
+    $tests['[user:cancel-url]'] = user_cancel_url($account);
+
+    // Generate tokens with interface language.
+    $link = url('user', array('absolute' => TRUE));
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('user' => $account), array('langcode' => $language->language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+      $this->assertTrue(strpos($output, $link) === 0, 'Generated URL is in interface language.');
+    }
+
+    // Generate tokens with the user's preferred language.
+    $edit['language'] = 'de';
+    $account = user_save($account, $edit);
+    $link = url('user', array('language' => $languages[$account->language], 'absolute' => TRUE));
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('user' => $account), array('callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+      $this->assertTrue(strpos($output, $link) === 0, "Generated URL is in the user's preferred language.");
+    }
+
+    // Generate tokens with one specific language.
+    $link = url('user', array('language' => $languages['de'], 'absolute' => TRUE));
+    foreach ($tests as $input => $expected) {
+      foreach (array($user1, $user2) as $account) {
+        $output = token_replace($input, array('user' => $account), array('langcode' => 'de', 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+        $this->assertTrue(strpos($output, $link) === 0, "Generated URL in in the requested language.");
+      }
+    }
+
   }
 }
 

+ 7 - 0
modules/user/user.test.orig

@@ -276,6 +276,7 @@ class UserValidationTestCase extends DrupalWebTestCase {
       'foo@example.com'        => array('Valid username', 'assertNull'),
       'foo@-example.com'       => array('Valid username', 'assertNull'), // invalid domains are allowed in usernames
       'þòøÇߪř€'               => array('Valid username', 'assertNull'),
+      'foo+bar'                => array('Valid username', 'assertNull'), // '+' symbol is allowed
       'ᚠᛇᚻ᛫ᛒᛦᚦ'                => array('Valid UTF8 username', 'assertNull'), // runes
       ' foo'                   => array('Invalid username that starts with a space', 'assertNotNull'),
       'foo '                   => array('Invalid username that ends with a space', 'assertNotNull'),
@@ -2386,7 +2387,13 @@ class UserUserSearchTestCase extends DrupalWebTestCase {
   }
 
   function testUserSearch() {
+    // Verify that a user without 'administer users' permission cannot search
+    // for users by email address. Additionally, ensure that the username has a
+    // plus sign to ensure searching works with that.
     $user1 = $this->drupalCreateUser(array('access user profiles', 'search content', 'use advanced search'));
+    $edit['name'] = 'foo+bar';
+    $edit['mail'] = $edit['name'] . '@example.com';
+    user_save($user1, $edit);
     $this->drupalLogin($user1);
     $keys = $user1->mail;
     $edit = array('keys' => $keys);