Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.641 diff -u -r1.641 common.inc --- includes/common.inc 15 May 2007 20:19:47 -0000 1.641 +++ includes/common.inc 20 May 2007 01:00:35 -0000 @@ -747,6 +747,87 @@ } /** + * Text replacement with deferred localization + * + * It can be used for the same base text to be localized to multiple languages + * or when the language is not yet known when composing the text. + * + * Example + * - This will print the same message in all available languages + * + * @code + * // This first line will be localized with the username parameter + * $text[] = array('Hello !username,', array('!username' => $user->name)); + * // This will be also localized, but the !content_type string has to be replaced later + * $text[] = array('A new !content_type has been posted to !site_name:'); + * // Simple string, won't be localized + * $text[] = $node->title + * + * // Variables for replacement + * $args['!site_name'] => variable_get('site_name', 'Drupal'); // Simple text, won't be localized + * $args['!content_type'] => array($node->type); // Array, will be localized + * + * // We have composed the base text, now it will be localized and printed for each language + * foreach (language_list() as $language) { + * $output .= '

'. $language->name.'

'; + * $output .= tt($text, $args, $language->language); + * } + * @endcode + * + * @param $text + * Plain text or array of text lines + * @param $args + * Optional associative array of replacements to make after translation. + * * Array keys are placeholders, like in t() function + * * Array values may be simple strings or an array with string and arguments when it must be localized + * @param $langcode + * Language code for localization + * @param $wrap + * Optional, TRUE for wrapping each text line + * @param $endline + * Optional string for end of each line + * @return + * Text localized and formatted with variable replacement + */ +function tt($text, $args = array(), $langcode = NULL, $wrap = FALSE, $endline = "\n") { + // If plain text, convert to array + $text = is_array($text) ? $text : array($text); + + // Prepare arguments for replacement + // This replacement also supports localization + foreach ($args as $key => $value) { + // If value is an array, it means it must go through localization + $value = is_array($value) ? t(array_shift($value), array_shift($value), $langcode) : $value; + switch ($key[0]) { + // Escaped only + case '@': + $args[$key] = check_plain($value); + break; + // Escaped and placeholder + case '%': + default: + $args[$key] = theme('placeholder', $value); + break; + // Pass-through + case '!': + $args[$key] = $value; + } + } + // Arguments have been parsed and localized + // Now we go with the text. + // Each line can be localized with its own parameters + foreach ($text as $key => $value) { + // It is an array, run through localization with optional parameters + $text[$key] = is_array($value) ? t(array_shift($value), array_shift($value), $langcode) : $value; + if ($wrap) { + $text[$key] = wordwrap($text[$key]); + } + } + // Put the text together and final variable replacement + return strtr(implode($endline, $text), $args); +} + +/** * @defgroup validation Input validation * @{ * Functions to validate user input. Index: modules/contact/contact.module =================================================================== RCS file: /cvs/drupal/drupal/modules/contact/contact.module,v retrieving revision 1.84 diff -u -r1.84 contact.module --- modules/contact/contact.module 14 May 2007 13:43:35 -0000 1.84 +++ modules/contact/contact.module 20 May 2007 01:00:36 -0000 @@ -364,33 +364,36 @@ $account = user_load(array('uid' => arg(1), 'status' => 1)); // Compose the body: - $message[] = "$account->name,"; - $message[] = t("!name (!name-url) has sent you a message via your contact form (!form-url) at !site.", array('!name' => $user->name, '!name-url' => url("user/$user->uid", array('absolute' => TRUE)), '!form-url' => url($_GET['q'], array('absolute' => TRUE)), '!site' => variable_get('site_name', 'Drupal'))); - $message[] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url("user/$account->uid", array('absolute' => TRUE)))); - $message[] = t('Message:'); + $message[] = "!name-from,"; + $message[] = array("!name-from (!name-from-url) has sent you a message via your contact form (!form-url) at !site."); + $message[] = array("If you don't want to receive such e-mails, you can change your settings at !url."); + $message[] = array('Message:'); $message[] = $form_values['message']; - // Tidy up the body: - foreach ($message as $key => $value) { - $message[$key] = wordwrap($value); - } - // Prepare all fields: $to = $account->mail; $from = $user->mail; // Format the subject: - $subject = '['. variable_get('site_name', 'Drupal') .'] '. $form_values['subject']; - - // Prepare the body: - $body = implode("\n\n", $message); - + $subject = '[!site-name] @subject'; + + // Prepare variables for replacement + $variables = array( + '!name-from' => $user->name, + '!name-to' => $account->name, + '!name-from-url' => url("user/$user->uid", array('absolute' => TRUE)), + '!form-url' => url($_GET['q'], array('absolute' => TRUE)), + '!site-name' => variable_get('site_name', 'Drupal'), + '!url' => url("user/$account->uid", array('absolute' => TRUE)), + '@subject' => $form_values['subject'] + ); + // Send the e-mail: - drupal_mail('contact-user-mail', $to, $subject, $body, $from); + user_mail('contact-mail', $account, $variables, $subject, $message, $from); // Send a copy if requested: if ($form_values['copy']) { - drupal_mail('contact-user-copy', $from, $subject, $body, $from); + user_mail('contact-copy', $user, $variables, $subject, $message, $from); } // Log the operation: Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.172 diff -u -r1.172 locale.module --- modules/locale/locale.module 15 May 2007 20:19:47 -0000 1.172 +++ modules/locale/locale.module 20 May 2007 01:00:37 -0000 @@ -186,10 +186,11 @@ * Implementation of hook_user(). */ function locale_user($type, $edit, &$user, $category = NULL) { - if ($type == 'form' && $category == 'account' && variable_get('language_count', 1) > 1 && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) == LANGUAGE_NEGOTIATION_PATH) { + // Show language field when user is editing own account and administrator is creating account + if (variable_get('language_count', 1) > 1 && ($type == 'register' && user_access('administer users') || $type == 'form' && $category == 'account' )) { $languages = language_list('enabled'); $languages = $languages['1']; - if ($user->language == '') { + if (!$user || $user->language == '') { $default = language_default(); $user->language = $default->language; } @@ -203,9 +204,9 @@ ); $form['locale']['language'] = array('#type' => 'radios', '#title' => t('Language'), - '#default_value' => $user->language, + '#default_value' => $user ? $user->language : $default->language, '#options' => $names, - '#description' => t('Selecting a different locale will change the interface language of the site.'), + '#description' => t('Selecting a locale will set the default site interface and mail language for this account.'), ); return $form; } Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.782 diff -u -r1.782 user.module --- modules/user/user.module 17 May 2007 21:33:59 -0000 1.782 +++ modules/user/user.module 20 May 2007 01:00:40 -0000 @@ -1192,16 +1192,13 @@ } function user_pass_submit($form_values, $form, &$form_state) { - global $base_url; $account = $form_values['account']; - $from = variable_get('site_mail', ini_get('sendmail_from')); // Mail one time login URL and instructions. - $variables = array('!username' => $account->name, '!site' => variable_get('site_name', 'Drupal'), '!login_url' => user_pass_reset_url($account), '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $account->mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE))); - $subject = _user_mail_text('pass_subject', $variables); - $body = _user_mail_text('pass_body', $variables); - $mail_success = drupal_mail('user-pass', $account->mail, $subject, $body, $from); + $variables = array('!login_url' => user_pass_reset_url($account)); + + $mail_success = user_mail('pass', $account, $variables); if ($mail_success) { watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail)); @@ -1216,6 +1213,63 @@ } /** + * Send an e-mail message to a user, using Drupal variables and default settings. + * This function automatically provides subject and body for known user e-mails + * + * @param $key + * A key to identify the mail sent. The mailkey for altering will be 'user-' + $key + * @param $account + * User account to which the e-mail will be sent. + * @param $subject + * Optional subject of the e-mail to be sent. This must not contain any newline + * characters, or the mail may not be sent properly. + * @param $body + * Optional message to be sent. Drupal will format the correct line endings for you. + * @param $from + * Optional mail to set From, Reply-To, Return-Path and Error-To to this value. + * @param $headers + * Optional associative array containing the headers to add. This is typically + * used to add extra headers (From, Cc, and Bcc). + * When sending mail, the mail must contain a From header. + * @return + * Returns TRUE if the mail was successfully accepted for delivery, FALSE otherwise. + */ +function user_mail($key, $account, $variables = array(), $subject = '', $body = '', $from = NULL, $headers = array()) { + global $base_url; + + // Add standard variables + $variables += array('!username' => $account->name, '!site' => variable_get('site_name', 'Drupal'), '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $account->mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE))); + + // Get the language code for this user account + $langcode = user_language($account); + + // Build the e-mail if no subject and body has been passed + $subject = $subject ? tt($subject, $variables, $langcode) : _user_mail_text($key.'_subject', $variables, $langcode); + $body = $body ? tt($body, $variables, $langcode, TRUE, "\n\n") : _user_mail_text($key.'_body', $variables, $langcode); + $from = $from ? $from : variable_get('site_mail', ini_get('sendmail_from')); + + return drupal_mail('user-'.$key, $account->mail, $subject, $body, $from, $headers); +} + +function _user_mail_localize($text) { + +} +/** + * Returns user language code for notifications + * + * @param $account + * Optional user account + */ +function user_language($account = NULL) { + global $user; + + $account = isset($account) ? $account : $user; + $default = language_default(); + // The user language will be the site default if no language is set for the account. + return (isset($account->language) && $account->language) ? $account->language : $default->language; +} + +/** * Menu callback; process one time login link and redirects to the user page on success. */ function user_pass_reset($uid, $timestamp, $hashed_pass, $action = NULL) { @@ -1341,7 +1395,6 @@ } function user_register_submit($form_values, $form, &$form_state) { - global $base_url; $admin = user_access('administer users'); $mail = $form_values['mail']; @@ -1353,7 +1406,7 @@ $pass = user_password(); }; $notify = isset($form_values['notify']) ? $form_values['notify'] : NULL; - $from = variable_get('site_mail', ini_get('sendmail_from')); + if (isset($form_values['roles'])) { $roles = array_filter($form_values['roles']); // Remove unset roles } @@ -1379,7 +1432,7 @@ watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit')); - $variables = array('!username' => $name, '!site' => variable_get('site_name', 'Drupal'), '!password' => $pass, '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE)), '!login_url' => user_pass_reset_url($account)); + $variables = array('!password' => $pass, '!mailto' => $mail, '!login_url' => user_pass_reset_url($account)); // The first user may login immediately, and receives a customized welcome e-mail. if ($account->uid == 1) { @@ -1399,19 +1452,14 @@ } else if (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) { // No e-mail verification is required, create new user account, and login user immediately. - $subject = _user_mail_text('welcome_subject', $variables); - $body = _user_mail_text('welcome_body', $variables); - drupal_mail('user-register-welcome', $mail, $subject, $body, $from); + user_mail('welcome', $account); user_authenticate($account->name, trim($pass)); $form_state['redirect'] = ''; return; } else if ($account->status || $notify) { // Create new user account, no administrator approval required. - $subject = $notify ? _user_mail_text('admin_subject', $variables) : _user_mail_text('welcome_subject', $variables); - $body = $notify ? _user_mail_text('admin_body', $variables) : _user_mail_text('welcome_body', $variables); - - drupal_mail(($notify ? 'user-register-notify' : 'user-register-welcome'), $mail, $subject, $body, $from); + user_mail(($notify ? 'admin' : 'welcome'), $account); if ($notify) { drupal_set_message(t('Password and further instructions have been e-mailed to the new user %user.', array('%user' => $name))); @@ -1424,11 +1472,8 @@ } else { // Create new user account, administrator approval required. - $subject = _user_mail_text('approval_subject', $variables); - $body = _user_mail_text('approval_body', $variables); - - drupal_mail('user-register-approval-user', $mail, $subject, $body, $from); - drupal_mail('user-register-approval-admin', $from, $subject, t("!username has applied for an account.\n\n!edit_uri", $variables), $from); + user_mail('approval', $account, $variables); + user_mail('approval-admin', user_load(1), $variables); drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, your password and further instructions have been sent to your e-mail address.')); } @@ -1679,7 +1724,7 @@ /*** Administrative features ***********************************************/ -function _user_mail_text($messageid, $variables = array()) { +function _user_mail_text($messageid, $variables = array(), $langcode = NULL) { // Check if an admin setting overrides the default string. if ($admin_setting = variable_get('user_mail_'. $messageid, FALSE)) { @@ -1689,21 +1734,23 @@ else { switch ($messageid) { case 'welcome_subject': - return t('Account details for !username at !site', $variables); + return t('Account details for !username at !site', $variables, $langcode); case 'welcome_body': - return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables); + return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables, $langcode); case 'admin_subject': - return t('An administrator created an account for you at !site', $variables); + return t('An administrator created an account for you at !site', $variables, $langcode); case 'admin_body': - return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables); + return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables, $langcode); case 'approval_subject': - return t('Account details for !username at !site (pending admin approval)', $variables); + return t('Account details for !username at !site (pending admin approval)', $variables, $langcode); case 'approval_body': - return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been granted, you may log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at !edit_uri\n\n\n-- !site team", $variables); + return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been granted, you may log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at !edit_uri\n\n\n-- !site team", $variables, $langcode); case 'pass_subject': - return t('Replacement login information for !username at !site', $variables); + return t('Replacement login information for !username at !site', $variables, $langcode); case 'pass_body': - return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables); + return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables, $langcode); + case 'approval-admin_subject': + return t("!username has applied for an account.\n\n!edit_uri", $variables, $langcode); } } }