Index: modules/contact/contact.module =================================================================== RCS file: /cvs/drupal/drupal/modules/contact/contact.module,v retrieving revision 1.86 diff -u -r1.86 contact.module --- modules/contact/contact.module 31 May 2007 12:03:18 -0000 1.86 +++ modules/contact/contact.module 3 Jun 2007 17:40:44 -0000 @@ -360,44 +360,46 @@ * Process the personal contact page form submission. */ function contact_mail_user_submit($form, &$form_state, $form_values) { - global $user; + global $user, $language; $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[] = $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); + + // Format the subject + $message['#subject'] = text_t('[!site_name] !contact_subject'); + // Compose the body + $message['#body'][] = text("!contact_name_to,"); + $message['#body'][] = text_t("!contact_name_from (!contact_from_url) has sent you a message via your contact form (!contact_form_url) at !site_name."); + $message['#body'][] = text_t("If you don't want to receive such e-mails, you can change your settings at !contact_to_url."); + $message['#body'][] = text_t('Message:'); + $message['#body'][] = $form_values['message']; + + // Add language independent variables and parameters to message + // The rest of the variables will be added in contact_mail_alter() once + // the language of the e-mail has been decided for each recipient. + + $message['#variables'] = array( + '!contact_subject' => $form_values['subject'], + '!contact_name_from' => $user->name, + '!contact_name_to' => $account->name, + ); + $message['#params'] = array( + 'user_from' => $user, + 'user_to' => $account, + ); + + // Send the e-mail to the recipient. The language will be the user's language or the site default + $message['#from'] = $user->mail; + user_mail('user-contact-mail', $account, $message); - // Send the e-mail: - drupal_mail('contact-user-mail', $to, $subject, $body, $from); - - // Send a copy if requested: + // Send a copy to the user, if requested. + // The default language for the copy will be the current page one. if ($form_values['copy']) { - drupal_mail('contact-user-copy', $from, $subject, $body, $from); + $message['#language'] = $language; + user_mail('user-contact-copy', $user, $message); } - // Log the operation: flood_register_event('contact'); watchdog('mail', '%name-from sent %name-to an e-mail.', array('%name-from' => $user->name, '%name-to' => $account->name)); - - // Set a status message: drupal_set_message(t('The message has been sent.')); // Jump to the user's profile page: @@ -406,6 +408,27 @@ } /** + * Implementation of hook_mail_alter(). + */ +function contact_mail_alter(&$message) { + switch ($message['#mail_id']) { + case 'user-contact-mail': + case 'user-contact-copy': + $user_from = $message['#params']['user_from']; + $user_to = $message['#params']['user_to']; + $tokens = array( + '!contact_from_url' => url("user/$user_from->uid", array('absolute' => TRUE, 'language' => $message['#language'])), + '!contact_url' => url("user/$user_to->uid", array('absolute' => TRUE, 'language' => $message['#language'])), + ); + // No break + case 'contact-page-mail': + $tokens['!contact_form_url'] = url($_GET['q'], array('absolute' => TRUE, 'language' => $message['#language'])); + + $message['#variables'] += $tokens; + } +} + +/** * Site-wide contact page */ function contact_site_page() { @@ -509,40 +532,51 @@ * Process the site-wide contact page form submission. */ function contact_mail_page_submit($form, &$form_state, $form_values) { - + global $language; + // E-mail address of the sender: as the form field is a text field, // all instances of \r and \n have been automatically stripped from it. $from = $form_values['mail']; - // Compose the body: - $message[] = t("!name sent a message using the contact form at !form.", array('!name' => $form_values['name'], '!form' => url($_GET['q'], array('absolute' => TRUE)))); - $message[] = $form_values['message']; - - // Tidy up the body: - foreach ($message as $key => $value) { - $message[$key] = wordwrap($value); - } - // Load the category information: $contact = db_fetch_object(db_query("SELECT * FROM {contact} WHERE cid = %d", $form_values['cid'])); - // Format the category: - $subject = t('[!category] !subject', array('!category' => $contact->category, '!subject' => $form_values['subject'])); - - // Prepare the body: - $body = implode("\n\n", $message); - - // Send the e-mail to the recipients: - drupal_mail('contact-page-mail', $contact->recipients, $subject, $body, $from); + // Compose the mail message: + $message['#subject'] = text_t('[!contact_category] !contact_subject'); + $message['#body'][] = text_t("!contact_name sent a message using the contact form at !contact_form_url."); + $message['#body'][] = $form_values['message']; + + // Collect variables for later replacement + $message['#variables'] = array( + '!contact_category' => $contact->category, + '!contact_subject' => $form_values['subject'], + '!contact_name' => $form_values['name'], + ); + + // Send the e-mail to the recipients using the default site language: + $message['#to'] = $contact->recipients; + $message['#from'] = $from; + $message['#language'] = language_default(); + + drupal_mail('contact-page-mail', $message); - // If the user requests it, send a copy. + // If the user requests it, send a copy using current language. if ($form_values['copy']) { - drupal_mail('contact-page-copy', $from, $subject, $body, $from); + $message['#to'] = $from; + $message['#langcode'] = $language->language; + + drupal_mail('contact-page-copy', $message); } // Send an auto-reply if necessary: if ($contact->reply) { - drupal_mail('contact-page-autoreply', $from, $subject, wordwrap($contact->reply), $contact->recipients); + $message['#mail_id'] = 'contact-page-autoreply'; + $message['#to'] = $from; + $message['body'][] = $contact->reply; + $message['#from'] = $contact->recipients; + $message['#langcode'] = $language->language; + + drupal_mail($message); } // Log the operation: Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.650 diff -u -r1.650 common.inc --- includes/common.inc 1 Jun 2007 09:49:11 -0000 1.650 +++ includes/common.inc 3 Jun 2007 17:40:43 -0000 @@ -2276,30 +2276,58 @@ * Send an e-mail message, using Drupal variables and default settings. * More information in the * PHP function reference for mail() - * @param $mailkey + * + * This function automatically renders the 'subject' and 'body' elements + * if present, using text rendering functions. + * + * @see drupal_render_text + * + * @see drupal_render_mail + * + * The mail data is an array that should have the following elements: + * + * #mail_id * A key to identify the mail sent, for altering. - * @param $to + * #to * The mail address or addresses where the message will be send to. The * formatting of this string must comply with RFC 2822. Some examples are: * user@example.com * user@example.com, anotheruser@example.com * User * User , Another User - * @param $subject - * 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 - * Message to be sent. Drupal will format the correct line endings for you. - * @param $from + * #subject + * Subject of the e-mail to be sent. It can be a text array for rendering + * or an plain text. This must not contain any newline characters, or the + * mail may not be sent properly. + * + * #body + * Message to be sent.It can be a plain text or an array for text rendering. + * Drupal will format the correct line endings for you. + * + * Optional elements + * + * #from * Sets From, Reply-To, Return-Path and Error-To to this value, if given. - * @param $headers + * #headers * 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. + * When sending mail, the mail must contain a From header. + * #language + * Language object to localize some parts of the e-mail + * #variables + * Tokens for replacement when rendering the body or the header + * #params + * Optional parameters to build the mail + * + * @param $message + * An array containing all the mail data. It supports text rendering for header + * and body. + * * @return Returns TRUE if the mail was successfully accepted for delivery, * FALSE otherwise. */ -function drupal_mail($mailkey, $to, $subject, $body, $from = NULL, $headers = array()) { +function drupal_mail($mail_id, &$message = array()) { + // Add default headers $defaults = array( 'MIME-Version' => '1.0', 'Content-Type' => 'text/plain; charset=UTF-8; format=flowed', @@ -2310,21 +2338,39 @@ // Return-Path headers should have a domain authorized to use the originating // SMTP server. Errors-To is redundant, but shouldn't hurt. $default_from = variable_get('site_mail', ini_get('sendmail_from')); + + // Add default properties + $message += array( + '#mail_id' => $mail_id, + '#from' => $default_from, + '#headers' => array(), + '#variables' => array(), + '#language' => language_default(), + ); + if ($default_from) { - $defaults['From'] = $defaults['Reply-To'] = $defaults['Sender'] = $defaults['Return-Path'] = $defaults['Errors-To'] = $default_from; - } - if ($from) { - $defaults['From'] = $defaults['Reply-To'] = $from; + $defaults['Sender'] = $defaults['Return-Path'] = $defaults['Errors-To'] = $default_from; } - $headers = array_merge($defaults, $headers); + $defaults['From'] = $defaults['Reply-To'] = $message['#from']; - // Bundle up the variables into a structured array for altering. - $message = array('#mail_id' => $mailkey, '#to' => $to, '#subject' => $subject, '#body' => $body, '#from' => $from, '#headers' => $headers); + $message['#headers'] += $defaults; + + // Allow modules to alter message and add their variables drupal_alter('mail', $message); + $mailkey = $message['#mail_id']; $to = $message['#to']; - $subject = $message['#subject']; - $body = $message['#body']; + + // We render subject and body. First add defaults for mail body + if (is_array($message['#body'])) { + // If the body is an array, may need defaults for rendering + $message['#body'] += array('#wordwrap' => 75, '#glue' => "\n\n"); + } + // We render the variables only once preparing them for substitution + $variables = drupal_render_arguments($message['#variables'], $message['#language']); + $subject = drupal_render_text($message['#subject'], $message['#language'], $variables); + $body = drupal_render_text($message['#body'], $message['#language'], $variables); + $from = $message['#from']; $headers = $message['#headers']; @@ -2599,6 +2645,206 @@ } /** + * Returns text lines ready for later rendering. + * + * This function outputs arrays containing text information + * ready for rendering with drupal_text_render() + * + * @see drupal_text_render() + * + * @param $value + * String containing the text to be localized or replaced and rendered + * @param $replace + * Whether to do text replacement with provided tokens + * @param $localize + * Whether to run the string through localization system + * @param $type + * Optional type of element, will default to 'string' + * @prefix + * Prefix for the string after rendering. Useful to compose html text. + * @sufix + * Sufix for the string after rendering. Useful to compose html text. + */ +function text($value, $replace = TRUE, $localize = FALSE, $type = 'string', $prefix = '', $sufix = '') { + return array( + '#type' => $type, + '#value' => $value, + '#replace' => $replace, + '#localize' => $localize, + '#prefix' => $prefix, + '#sufix' => $sufix + ); +} + +/** + * Shorthand function for translatable text lines + * + * This is only a wrapper for text() function for quick writing + * and for marking the strings to be localized for the locale extractor. + * + * @see text() + * + * @param $value + * String to be localized and then replaced with tokens + */ +function text_t($value) { + return text($value, TRUE, TRUE); +} + +/** + * Render different types of text arrays into strings. + * + * This function should be used for composing complex texts or email texts. Texts are + * composed using arrays, similar to forms, with some parameters, and then rendered using + * this function. It supports localization of single text lines and also + * has some internal caching allowing quick localization of a single text to multiple + * languages. + * + * Example to print the same message in all available languages: + * + * @code + * // Delayed localization with parameter substitution + * + * // There are other types of arrays that may be rendered so we have to state the type. + * // Each array line will be rendered as a string and glued together at the end. + * + * // We set the text type for it to be rendered right + * $text['#type'] = 'text'; + * // This text will be localized and replaced with variables + * $text[] = text_t('Hello !username,'); + * // This text will be localized and replaced with variables + * $text[] = text_t('A new !content_type has been posted to !site_name:'); + * // Simple string, won't be localized nor replaced with variables + * $text[] = $node->title.'.'; + * // This text will be replaced but not localized + * $text[] = text('!site_slogan'); + * + * // This one is more tricky. A variable whose default will be localized when rendering + * $text[] => array( + * '#type' => 'variable', + * '#name' => 'site_slogan', + * '#default'=> text_t('Drupal rocks!'), // We can nest arguments and text arrays !! + * ); + * // Special formats for the text + * $text['#glue'] = ' '; // Will be used for gluing the lines together + * // Variables for replacement + * $args['!site_name'] => variable_get('site_name', 'Drupal'); // Simple text, won't be localized + * $args['!content_type'] => text_t($node->type); // The variable will be localized + * $args['!username'] = $user->name; + * + * // Localize for all languages + * $output = ''; + * foreach (language_list() as $language) { + * $output .= $language->name .':'. drupal_render_text($text, $args, $language->language); + * } + * + * @endcode + * + * @see text() + * + * @see text_t() + * + * @param $text + * Text to be rendered in array form + * @param $langcode + * Optional language code, for language different to current one + * @param $variables + * Arguments ready for replacement + * @param $defaults + * Internal use only + * + * @return + * Plain text rendered according to parameters. + */ +function drupal_render_text(&$text, $language, $variables = array(), $defaults = array(), $debug = FALSE) { + static $text_defaults = array('#type' => 'text', '#value' => '', '#replace' => FALSE, '#localize' => FALSE, + '#wordwrap' => 0, '#glue' => "\n", '#param' => array(), '#rendered' => array(), + '#prefix' => '', '#sufix' => '', '#cache' => FALSE + ); + + // Add defaults. If plain text, or no text type return value + if (!is_array($text)) { + $text = array('#type' => 'value', '#value' => $text); + } + + // Add defaults before processing, in the right order for overriding + $text += $defaults + $text_defaults; + + // If already rendered for this language, and is cacheable just return value + if ($text['#cache'] && isset($text['#rendered'][$language->language])) { + return $text['#rendered'][$languag->language]; + } + + // Get value depending on type + switch ($text['#type']) { + case 'text': + // Array of text lines, render each line and merge + foreach (element_children($text) as $index) { + // We render each text line passing the wordwrap paramater + $value[] = drupal_render_text($text[$index], $language, $variables, array('#wordwrap' => $text['#wordwrap']), $debug); + } + // Now we glue the text together + $value = implode($text['#glue'], $value); + break; + case 'variable': + // The default value may need to be localized so we render it + $value = variable_get($text['#name'], drupal_render_text($text['#default'], $language, array(), array(), $debug)); + break; + case 'value': + case 'string': + default: + $value = $text['#value']; + break; + } + + // Process optional parameters. Localize, replace, wordwrap.... + if ($text['#localize']) { + $value = t($value, array(), $language->language); + } + if ($text['#replace'] && $variables) { + $value = strtr($value, $variables); + } + if ($text['#wordwrap']) { + $value = wordwrap($value, $text['#wordwrap']); + } + $value = $text['#prefix'] . $value . $text['#sufix']; + + // We cache this text for this language unless specified otherwise + if ($text['#cache']) { + $text['#rendered'][$language->language] = $value; + } + // Debug support, temporary + if ($debug) drupal_set_message("drupal_render_text: type=".$text['#type'].' value='.$text['#value'].' result='.$value); + + return $value; +} + +/** + * Render an array of text arguments for replacement + */ +function drupal_render_arguments($args, $language) { + $variables = array(); + // Render variables for replacement, values will be also rendered + // We pass the full variables array, to the next one to allow complex composition + foreach (element_children($args) as $name) { + $value = drupal_render_text($args[$name], $language, $variables); + switch ($name[0]) { + case '@': + $variables[$name] = check_plain($value); + break; + case '%': + $variables[$name] = theme('placeholder', $value); + break; + case '!': + default: + // Just asign result + $variables[$name] = $value; + } + } + return $variables; +} + +/** * Function used by uasort in drupal_render() to sort structured arrays * by weight. */ Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.177 diff -u -r1.177 locale.module --- modules/locale/locale.module 30 May 2007 08:08:58 -0000 1.177 +++ modules/locale/locale.module 3 Jun 2007 17:40:45 -0000 @@ -186,12 +186,14 @@ * 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) { + global $language; + 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 == '') { - $user->language = language_default('language'); - } + + // If user ir being created, so we set user language to the current page one + $user_language = $user ? user_language($user) : $language; + $names = array(); foreach ($languages as $langcode => $language) { $names[$langcode] = t($language->name) .' ('. $language->native .')'; @@ -200,11 +202,12 @@ '#title' => t('Interface language settings'), '#weight' => 1, ); + $form['locale']['language'] = array('#type' => 'radios', '#title' => t('Language'), - '#default_value' => $user->language, + '#default_value' => $user_language->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.788 diff -u -r1.788 user.module --- modules/user/user.module 30 May 2007 08:08:59 -0000 1.788 +++ modules/user/user.module 3 Jun 2007 17:40:48 -0000 @@ -1205,6 +1205,67 @@ } /** + * Send an e-mail message to a user account or a destination mail address. + * + * This is basically a wrapper for drupal_mail(), but some predefined variables + * are added and when the destination mail address belongs to a user, some properties + * are pulled from the user account. + * + * @see drupal_mail() + * + * @param $destination + * User account or e-mail address to which the e-mail will be sent. + * @param $message + * Message array. + * @return + * Returns TRUE if the mail was successfully accepted for + * delivery, FALSE otherwise. + */ +function user_mail($mail_id, $destination, &$message = array()) { + // Get user account for destination + $account = is_object($destination) ? $destination : user_load(array('mail' => $destination)); + + // Get language from user account or from parameters. If the user has a language + // that will be it. If not, a language may be passed depending on which type of mail + // it is. I.e. for password recovery page, it will be current language, but for contact + // messages it will be the site default. + if ($account && $account->language) { + $language = user_language($account); + } else { + $language = isset($message['#language']) ? $message['#language'] : language_default(); + } + + // Add properties to message array + $message['#to'] = $mail = $account ? $account->mail : $destination; + $message['#language'] = $language; + $message['#user'] = $account; + + // Further e-mail processing, rendering, etc, and send e-mail + return drupal_mail($mail_id, $message); +} + +/** + * Returns the code of the language preferred by the + * user if set or the default language code. + * + * @param $account + * Optional user account. Falls back to the current $user. + */ +function user_language($account = NULL) { + global $user; + + $account = isset($account) ? $account : $user; + + // The user language will be the site default if no language is set for the account. + if (isset($account->language) && $account->language) { + $language_list = language_list(); + return $language_list[$account->language]; + } else { + return language_default(); + } +} + +/** * 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) { @@ -1386,7 +1447,7 @@ } 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. - _user_mail_notify('register_no_approval_required', $account, $pass); + _user_mail_notify('register_no_approval_required', $account, array('!password' => $pass)); user_authenticate($account->name, trim($pass)); $form_state['redirect'] = ''; return; @@ -1394,7 +1455,7 @@ else if ($account->status || $notify) { // Create new user account, no administrator approval required. $op = $notify ? 'register_admin_created' : 'register_no_approval_required'; - _user_mail_notify($op, $account, $pass); + _user_mail_notify($op, $account, array('!password' => $pass)); if ($notify) { drupal_set_message(t('Password and further instructions have been e-mailed to the new user %user.', array('%user' => $name))); } @@ -1406,9 +1467,8 @@ } else { // Create new user account, administrator approval required. - _user_mail_notify('register_pending_approval', $account, $pass); + _user_mail_notify('register_pending_approval', $account, array('!password' => $pass)); 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.')); - } } } @@ -1658,45 +1718,63 @@ /*** Administrative features ***********************************************/ -function _user_mail_text($messageid, $variables = array()) { - - // Check if an admin setting overrides the default string. - if ($admin_setting = variable_get('user_mail_'. $messageid, FALSE)) { - return strtr($admin_setting, $variables); - } - // No override, return with default strings. - else { - switch ($messageid) { - case 'register_no_approval_required_subject': - return t('Account details for !username at !site', $variables); - case 'register_no_approval_required_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); - case 'register_admin_created_subject': - return t('An administrator created an account for you at !site', $variables); - case 'register_admin_created_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); - case 'register_pending_approval_subject': - return t('Account details for !username at !site (pending admin approval)', $variables); - case 'register_pending_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 approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n-- !site team", $variables); - case 'password_reset_subject': - return t('Replacement login information for !username at !site', $variables); - case 'password_reset_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); - case 'status_activated_subject': - return t('Account details for !username at !site (approved)', $variables); - case 'status_activated_body': - return "!username,\n\nYour account at !site has been activated.\n\nYou may now 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\nOnce you have set your own password, you will be able to log in to !login_uri in the future using the following username:\n\nusername: !username\n"; - case 'status_blocked_subject': - return t('Account details for !username at !site (blocked)', $variables); - case 'status_blocked_body': - return "!username,\n\nYour account on !site has been blocked."; - case 'status_deleted_subject': - return t('Account details for !username at !site (deleted)', $variables); - case 'status_deleted_body': - return "!username,\n\nYour account on !site has been deleted."; - } +/** + * Generate text for the built in emails sent in user.module. + * + * @param $messageid + * Message identifier as used in user_mail(). + * @param $variables + * Values to replace placeholders in the text with. + */ +function _user_mail_text($messageid) { + // Get default text. We use text_t() to mark text for the locale extractor and because + // the text will be localized later using drupal_render_text(). + + switch ($messageid) { + case 'register_no_approval_required_subject': + $default = text_t('Account details for !user_name at !site_name'); + break; + case 'register_no_approval_required_body': + $default = text_t("!user_name,\n\nThank you for registering at !site_name. You may now log in to !login_uri using the following username and password:\n\nusername: !user_name\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!user_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 !user_edit_uri so you can change your password.\n\n\n-- !site_name team"); + break; + case 'register_admin_created_subject': + $default = text_t('An administrator created an account for you at !site_name'); + break; + case 'register_admin_created_body': + $default = text_t("!user_name,\n\nA site administrator at !site_name has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !user_name\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!user_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 !user_edit_uri so you can change your password.\n\n\n-- !site_name team"); + break; + case 'register_pending_approval_subject': + $default = text_t('Account details for !user_name at !site_name (pending admin approval)'); + break; + case 'register_pending_approval_body': + $default = text_t("!user_name,\n\nThank you for registering at !site_name. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n-- !site_name team"); + break; + case 'password_reset_subject': + $default = text_t('Replacement login information for !user_name at !site_name'); + break; + case 'password_reset_body': + $default = text_t("!user_name,\n\nA request to reset the password for your account has been made at !site_name.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!user_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 !user_edit_uri so you can change your password."); + break; + case 'status_activated_subject': + $default = text_t('Account details for !user_name at !site_name (approved)'); + break; + case 'status_activated_body': + $default = text_t("!user_name,\n\nYour account at !site_name has been activated.\n\nYou may now log in by clicking on this link or copying and pasting it in your browser:\n\n!user_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 !user_edit_uri so you can change your password.\n\nOnce you have set your own password, you will be able to log in to !login_uri in the future using the following username:\n\nusername: !user_name\n"); + break; + case 'status_blocked_subject': + $default = text_t('Account details for !user_name at !site_name (blocked)'); + break; + case 'status_blocked_body': + $default = text_t("!user_name,\n\nYour account on !site_name has been blocked."); + break; + case 'status_deleted_subject': + $default = text_t('Account details for !user_name at !site_name (deleted)'); + break; + case 'status_deleted_body': + $default = text_t("!user_name,\n\nYour account on !site_name has been deleted."); + break; } + return array('#type' => 'variable', '#name' => 'user_mail_'. $messageid, '#default' => $default); } function user_admin_check_user() { @@ -2450,7 +2528,7 @@ ); // These email tokens are shared for all settings, so just define // the list once to help ensure they stay in sync. - $email_token_help = t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.'; + $email_token_help = t('Available variables are:') .' !user_name, !site_name, !password, !uri_full, !uri_brief, !mailto, !date, !login_uri, !user_edit_uri, !user_login_url.'; $form['email']['admin_created'] = array( '#type' => 'fieldset', @@ -3071,83 +3149,103 @@ } /** - * Return an array of token to value mappings for user e-mail messages. - * - * @param $account - * The user object of the account being notified. Must contain at - * least the fields 'uid', 'name', and 'mail'. - * @param $password - * Optional string containing the user's current password (if known). - * @return - * Array of mappings from token names to values (for use with strtr()). + * Implementation of hook_mail_tokens(). */ -function user_mail_tokens($account, $password = NULL) { +function user_mail_alter(&$message) { global $base_url; + + $language = isset($message['#language']) ? $message['#language'] : language_default(); + + // Site-wide tokens $tokens = 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)), + '!site_name' => variable_get('site_name', 'Drupal'), + '!site_uri_full' => $base_url, + '!site_uri_brief' => substr($base_url, strlen('http://')), + '!date' => format_date(time()), + '!message_mailto' => $message['#to'], ); - if (!empty($password)) { - $tokens['!password'] = $password; + + // User dependent values. We make a distinction between the user receiving the e-mail + // and the account the e-mail is about + if (isset($message['#account']) && $account = $message['#account']) { + $tokens += array( + '!account_name' => $account->name, + '!account_login_url' => user_pass_reset_url($account), + '!account_edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE, 'language' => $language)), + ); } - return $tokens; + if (isset($message['#user']) && $user = $message['#user']) { + $tokens += array( + '!user_name' => $user->name, + '!user_login_url' => user_pass_reset_url($user), + '!user_edit_uri' => url('user/'. $user->uid .'/edit', array('absolute' => TRUE, 'language' => $language)), + ); + } + $message['#variables'] += $tokens; + var_dump($message); } /** * Conditionally create and send a notification email when a certain * operation happens on the given user account. * + * @see user_mail() * @see user_mail_tokens() * @see drupal_mail() * * @param $op - * The operation being performed on the account. Possible values: - * 'register_admin_created': Welcome message for user created by the admin - * 'register_no_approval_required': Welcome message when user self-registers - * 'register_pending_approval': Welcome message, user pending admin approval - * 'password_reset': Password recovery request - * 'status_activated': Account activated - * 'status_blocked': Account blocked - * 'status_deleted': Account deleted + * The operation being performed on the account. Possible values: + * 'register_admin_created': Welcome message for user created by the admin + * 'register_no_approval_required': Welcome message when user self-registers + * 'register_pending_approval': Welcome message, user pending admin approval + * 'password_reset': Password recovery request + * 'status_activated': Account activated + * 'status_blocked': Account blocked + * 'status_deleted': Account deleted * * @param $account - * The user object of the account being notified. Must contain at - * least the fields 'uid', 'name', and 'mail'. + * The user object of the account being notified. Must contain at + * least the fields 'uid', 'name', and 'mail'. * - * @param $password - * Optional string containing the user's current password (if known). + * @param $variables + * Optional variables for text replacement * * @return - * The return value from drupal_mail(), if ends up being called. + * The return value from drupal_mail(), if ends up being called. */ -function _user_mail_notify($op, $account, $password = NULL) { +function _user_mail_notify($op, $account, $variables) { + global $language; + $mail_id = 'user-'. strtr($op, '_', '-'); + if ($op == 'register_pending_approval') { // Special case, since we need to distinguish what we send to the // user and what we send to the administrator, handled below. $mail_id .= '-user'; } + // By default, we always notify except for deleted and blocked. $default_notify = ($op != 'status_deleted' && $op != 'status_blocked'); $notify = variable_get('user_mail_'. $op .'_notify', $default_notify); $result = NULL; + if ($notify) { - $from = variable_get('site_mail', ini_get('sendmail_from')); - $variables = user_mail_tokens($account, $password); - $subject = _user_mail_text($op .'_subject', $variables); - $body = _user_mail_text($op .'_body', $variables); - $result = drupal_mail($mail_id, $account->mail, $subject, $body, $from); + $message['#subject'] = _user_mail_text($op .'_subject'); + $message['#body'][] = _user_mail_text($op .'_body'); + $message['#variables'] = $variables; + $message['#account'] = $account; + // This is the only case when the e-mail should be in the current language instead of default + $message['#language'] = ($op == 'password_reset') ? $language : language_default(); + $result = user_mail($mail_id, $account, $message); if ($op == 'register_pending_approval') { // If a user registered requiring admin approval, notify the admin, too. - drupal_mail('user-register-approval-admin', $from, $subject, t("!username has applied for an account.\n\n!edit_uri", $variables), $from); + // The admin mail should be in the admin's language or site default language + $message['#language'] = language_default(); + $message['#body'][] = text_t("!account_name has applied for an account."); + $message['#body'][] = text("!account_edit_uri"); + user_mail('user-register-approval-admin', variable_get('site_mail', ini_get('sendmail_from')), $message); } } + return $result; }