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 2 Jun 2007 16:44:06 -0000
@@ -363,41 +363,40 @@
global $user;
$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'];
+
+ // Format the subject
+ $message['subject'] = text_t('[!site_name] !subject');
+ // Compose the body
+ $message['body'][] = text("!name-to,");
+ $message['body'][] = text_t("!name-from (!name-from-url) has sent you a message via your contact form (!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 !url.");
+ $message['body'][] = text_t('Message:');
+ $message['body'][] = $form_values['message'];
+
+ // Prepare variables for replacement. User mail tokens will be
+ // added automatically in user_mail, so they're not needed here.
+ $message['variables'] = array(
+ '!name-from' => $user->name,
+ '!name-to' => $account->name,
+ '!name-from-url' => array('#type' => 'url', 'url' => "user/$user->uid", 'options' => array('absolute' => TRUE)),
+ '!form-url' => array('#type' => 'url', 'url' => $_GET['q'], 'options' => array('absolute' => TRUE)),
+ '!url' => array('#type' => 'url', 'url' => "user/$account->uid", 'options' => array('absolute' => TRUE)),
+ '!subject' => $form_values['subject'],
+ );
+
+ // Send the e-mail
+ $message['#mail_id'] = 'user-contact-mail';
+ $message['#from'] = $user->mail;
+ user_mail($account, $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);
-
- // 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
if ($form_values['copy']) {
- drupal_mail('contact-user-copy', $from, $subject, $body, $from);
+ $message['#mail_id'] = 'user-contact-copy';
+ user_mail($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:
@@ -509,40 +508,54 @@
* 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('[!category] !subject');
+ $message['body'][] = text_t("!name sent a message using the contact form at !form.");
+ $message['body'][] = $form_values['message'];
+
+ // Collect variables for later replacement
+ $message['variables'] = array(
+ '!category' => $contact->category,
+ '!subject' => $form_values['subject'],
+ '!name' => $form_values['name'],
+ '!form' => array('#type' => 'url', 'url' => $_GET['q'], 'options' => array('absolute' => TRUE)),
+ );
+
+ // Send the e-mail to the recipients using the default site language:
+ $message['#mail_id'] = 'contact-page-mail';
+ $message['#to'] = $contact->recipients;
+ $message['#from'] = $from;
+ $message['#langcode'] = language_default('language');
+
+ drupal_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['#mail_id'] = 'contact-page-copy';
+ $message['#to'] = $from;
+ $message['#langcode'] = $language->language;
+
+ drupal_mail($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 2 Jun 2007 16:44:06 -0000
@@ -2276,30 +2276,57 @@
* 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 or '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
+ * If the element 'subject' exists it will be rendered before sending
+ * the email and '#subject' will be populated from that
+ * #body or 'body'
* Message to be sent. Drupal will format the correct line endings for you.
- * @param $from
+ * If the element 'body' exists it will be rendered before sending the email
+ * and '#body' will be populated from that
+ *
+ * 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.
+ * #langcode
+ * Language code to localize some parts of the e-mail
+ * 'variables'
+ * Tokens for replacement when rendering the body or the header
+ *
+ * @param $message
+ * An array containing all the mail data. It supports text rendering for parts
+ * of the e-mail.
+ *
* @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) {
+ // Add default headers
$defaults = array(
'MIME-Version' => '1.0',
'Content-Type' => 'text/plain; charset=UTF-8; format=flowed',
@@ -2310,17 +2337,30 @@
// 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
+ $mail += array(
+ '#from' => $default_from,
+ '#headers' => array(),
+ );
+
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'] = $mail['#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);
+ $mail['#headers'] += $defaults;
+
+ // Alter before text rendering
+ drupal_alter('mail_text', $mail);
+
+ // Render different parts of the mail. From here on, whe don't use the by-reference $mail
+ // variable anymore but the resulting array with just known mail properties.
+ // However, the last header and body rendered will be in $mail['#header'] and $mail['#body']
+ $message = drupal_render_mail($mail);
+
+ // Alter after text rendering
drupal_alter('mail', $message);
+
$mailkey = $message['#mail_id'];
$to = $message['#to'];
$subject = $message['#subject'];
@@ -2599,6 +2639,300 @@
}
/**
+ * 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.
+ * // Type 'text' arrays will be rendered as an array of strings glued together at the end.
+ * $text['#type'] = 'text';
+ *
+ * $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');
+ *
+ * // 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;
+ *
+ * // This one is more tricky. A variable whose default will be localized when rendering
+ * $args['!site_slogan'] => array(
+ * '#type' => 'variable',
+ * 'name' => 'site_slogan',
+ * 'default'=> text_t('Drupal rocks!'), // We can nest arguments and text arrays !!
+ * );
+ *
+ * // 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
+ * @param $debug
+ * Developer facility, for debugging
+ * @return
+ * Plain text or array of plain texts, depending on text type.
+ */
+function drupal_render_text(&$text, $langcode = NULL, $variables = array(), $defaults = array(), $debug = FALSE) {
+ global $language;
+ static $text_defaults = array( // Defaults for each type of content, 'all' for all types
+ 'all' => array('#value' => '', '#replace' => FALSE, '#localize' => FALSE, '#wordwrap' => 0, '#glue' => '',
+ '#param' => array(), '#rendered' => array(), '#prefix' => '', '#sufix' => '', '#cache' => TRUE
+ ),
+ 'variable' => array('#callback' => 'variable_get'),
+ 'date' => array('#callback' => 'format_date'),
+ 'interval' => array('#callback' => 'format_interval'),
+ 'plural' => array('#callback' => 'format_plural'),
+ 'url' => array('#callback' => 'url', '#language' => 'options'), // Language in array 'options'
+ 'link' => array('#callback' => 'l', '#language' => 'options'), // Language in array 'options'
+ );
+ static $callback_parameters = array( // Parameters for known callbacks
+ 'url' => array('url' => NULL, 'options' => array()),
+ 'l' => array('text' => NULL, 'path' => NULL, 'options' => array()),
+ 'variable_get' => array('name' => NULL, 'default' => NULL),
+ 'format_date' => array('timestamp' => NULL, 'type' => 'medium', 'format' => '', 'timezone' => NULL, 'langcode' => array('#type' => 'langcode')),
+ 'format_interval' => array('timestamp' => NULL, 'granularity' => 2, 'langcode' => array('#type' => 'langcode')),
+ 'format_plural' => array('count' => NULL, 'singular' => NULL, 'plural' => NULL, 'args' => array(), 'langcode' => array('#type' => 'langcode')),
+ );
+ static $language_list; // We cache the language list too
+ if (empty($language_list)) {
+ $language_list = language_list();
+ }
+ $langcode = $langcode ? $langcode : $language->language;
+
+ // Add defaults. If plain text, convert to string array
+ if (!is_array($text)) {
+ $text = array('#value' => $text, '#type' => 'string');
+ }
+ // If no type, it is a value. May be a parameter for callbacks
+ if (empty($text['#type'])) {
+ $text = array('#type' => 'value', '#value' => $text);
+ }
+
+ // If already rendered for this language, just return value
+ if (isset($text['#rendered'][$langcode])) {
+ return $text['#rendered'][$langcode];
+ }
+
+ // Add defaults before processing, in the right order for overriding
+ $text += $defaults;
+ $text += $text_defaults['all'];
+ // We should already have a type property. Set defaults for this type
+ $type = $text['#type'];
+ if (isset($text_defaults[$type])) {
+ $text += $text_defaults[$type];
+ }
+
+ if($debug) drupal_set_message(print_r($text, TRUE));
+ // So we need some rendering, first we process variables for replacement
+
+ // Get value depending on type
+ switch ($text['#type']) {
+ case 'langcode':
+ // This is a langcode parameter of a callback
+ $value = $langcode;
+ break;
+ case 'text':
+ // Array of text lines, render each line and merge
+ foreach (element_children($text) as $index) {
+ $value[] = drupal_render_text($text[$index], $langcode, $variables, $defaults, $debug);
+ }
+ // Now we glue the text together
+ $value = implode($text['#glue'], $value);
+ break;
+ case 'arguments':
+ // Render variables for replacement
+ // We pass the full variables array, to the next
+ foreach (element_children($text) as $name) {
+ $value[$name] = drupal_render_text($text[$name], $langcode, $variables);
+ switch ($name[0]) {
+ case '@':
+ $value[$name] = check_plain($value[$name]);
+ break;
+ case '%':
+ $value[$name] = theme('placeholder', $value[$name]);
+ break;
+ case '!':
+ // Do nothing
+ }
+ // Add to variables to pass over to the next
+ $variables[$name] = $value[$name];
+ }
+ break;
+ case 'value':
+ case 'string':
+ default:
+ // Prepare callbacks and parameters
+ if (isset($text['#callback']) && ($function = $text['#callback']) && function_exists($function)) {
+ // Prepare parameters
+ if(isset($text['#parameters'])) {
+ // It's the first one of this type, should bring information about parameters
+ // We get it and store it in the static variable
+ $parameters = $callback_parameters[$function] = $text['#parameters'];
+ } elseif (isset($callback_parameters[$function])) {
+ $parameters = $callback_parameters[$function];
+ }
+ // Collect parameters. We render them with drupal_render_text to support complex parameters
+ foreach ($parameters as $name => $value) {
+ $params[$name] = isset($text[$name]) ? drupal_render_text($text[$name], $langcode, $variables, $defaults) : drupal_render_text($parameters[$name]);
+ }
+ // Replace language for functions needing this parameter
+ if (isset($text['#language'])) {
+ $params[$text['#language']]['language'] = $language_list[$langcode];
+ }
+ $value = call_user_func_array($function, $params);
+
+ } else {
+ $value = $text['#value'];
+ }
+ break;
+ }
+
+ // Process optional parameters. Localize, replace, wordwrap....
+ // Make sure we are not screwing with arrays, just text
+ if (!is_array($value)) {
+ if ($text['#localize']) {
+ $value = t($value, array(), $langcode);
+ }
+ if ($text['#replace'] && $variables) {
+ $value = strtr($value, $variables);
+ }
+ if ($text['#wordwrap']) {
+ $value = wordwrap($value, $text['#wordwrap']);
+ }
+ $value = $text['#prefix'] . $value . $text['#sufix'];
+ $output = $value;
+ } else {
+ $output = print_r($value, TRUE);
+ }
+ // For now, this function supports debugging
+
+ if ($debug) drupal_set_message("Text rendering: type=".$text['#type']."value=".$text['#value']." return= $output");
+
+ // We cache this text for this language unless specified otherwise
+ if ($text['#cache']) {
+ $text['#rendered'][$langcode] = $value;
+ }
+ return $value;
+}
+
+/**
+ * Render a mail array doing text rendering for known parts of the mail
+ *
+ * @see drupal_mail()
+ */
+function drupal_render_mail(&$mail) {
+ global $language;
+
+ $langcode = isset($mail['#langcode']) ? $mail['#langcode'] : $language->language;
+
+ // This is an e-mail. Has some known parts like
+ // $mail['body'] and $mail['subject'] which are the texts before rendering
+
+ // First, compute variables for substitution
+ $variables = isset($mail['variables']) ? drupal_render_text($mail['variables'] += array('#type' => 'arguments'), $langcode, array(), array('#type' => 'value')) : array();
+
+ // Subject and body mail be properties for rendering or they may be fixed values
+ // passed in '#subject' and '#body' elements
+ if (isset($mail['subject'])) {
+ $mail['subject'] = is_array($mail['subject']) ? $mail['subject'] + array('#type' => 'text') : array('#type' => 'string', '#value' => $mail['subject']);
+ $mail['#subject'] = drupal_render_text($mail['subject'], $langcode, $variables);
+ }
+
+ if (isset($mail['body'])) {
+ $mail['body'] = is_array($mail['body']) ? $mail['body'] + array('#type' => 'text', '#glue' => "\n\n") : array('#type' => 'string', '#value' => $mail['body']);
+ $mail['#body'] = drupal_render_text($mail['body'], $langcode, $variables, array('#wordwrap' => 75));
+ }
+
+ // Bundle up the variables into a structured array for altering removing non-mail ones.
+ // This will produce at least some notice if any property is missing.
+ foreach (array('#mail_id', '#to', '#subject', '#body', '#from', '#headers') as $key) {
+ $message[$key] = $mail[$key];
+ }
+ // The return value is the whole array
+ return $message;
+}
+
+/**
* 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 2 Jun 2007 16:44:07 -0000
@@ -186,10 +186,9 @@
* 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) {
- $languages = language_list('enabled');
+ 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 && empty($user->language)) {
$user->language = language_default('language');
}
$names = array();
@@ -202,9 +201,9 @@
);
$form['locale']['language'] = array('#type' => 'radios',
'#title' => t('Language'),
- '#default_value' => $user->language,
+ '#default_value' => user_language($user),
'#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 2 Jun 2007 16:44:11 -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($destination, &$message) {
+
+ // Now we get the destination mail or other properties
+ if ($account = is_object($destination) ? $destination : user_load(array('mail' => $destination))) {
+ // Destination was a user account or is resolved to a user account.
+ $mail = $account->mail;
+ $langcode = user_language($account);
+ $variables = user_mail_tokens($account);
+ }
+ else {
+ // Destination is an e-mail address and it doesn't belong to a known user.
+ $mail = $destination;
+ $variables = array('!mailto' => $mail) + user_mail_tokens();
+ }
+
+ // Add parameters to $message array. These may need to be overridden from one email
+ // to the next when sending an e-mail to different users.
+ // We add some extra properties that may be useful for mail_text_alter()
+ $message = array_merge($message, array(
+ '#to' => $mail,
+ '#langcode' => $account ? user_language($account) : language_default('language'),
+ '#account' => $account,
+ 'variables' => isset($message['variables']) ? $variables + $message['variables'] : $variables,
+ ));
+
+ return drupal_mail($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.
+ return (isset($account->language) && $account->language) ? $account->language : 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) {
@@ -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 !username at !site_name');
+ break;
+ case 'register_no_approval_required_body':
+ $default = text_t("!username,\n\nThank you for registering at !site_name. 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_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("!username,\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: !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_name team");
+ break;
+ case 'register_pending_approval_subject':
+ $default = text_t('Account details for !username at !site_name (pending admin approval)');
+ break;
+ case 'register_pending_approval_body':
+ $default = text_t("!username,\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 !username at !site_name');
+ break;
+ case 'password_reset_body':
+ $default = text_t("!username,\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!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.");
+ break;
+ case 'status_activated_subject':
+ $default = text_t('Account details for !username at !site_name (approved)');
+ break;
+ case 'status_activated_body':
+ $default = text_t("!username,\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!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");
+ break;
+ case 'status_blocked_subject':
+ $default = text_t('Account details for !username at !site_name (blocked)');
+ break;
+ case 'status_blocked_body':
+ $default = text_t("!username,\n\nYour account on !site_name has been blocked.");
+ break;
+ case 'status_deleted_subject':
+ $default = text_t('Account details for !username at !site_name (deleted)');
+ break;
+ case 'status_deleted_body':
+ $default = text_t("!username,\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:') .' !username, !site_name, !password, !uri_full, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.';
$form['email']['admin_created'] = array(
'#type' => 'fieldset',
@@ -3074,28 +3152,31 @@
* 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).
+ * Optional user object of the account being notified. Must contain
+ * at least the 'uid', 'name', and 'mail' fields. If not provided,
+ * the function will only return site-wide mappings.
* @return
- * Array of mappings from token names to values (for use with strtr()).
+ * Array of mappings from token names to values.
*/
-function user_mail_tokens($account, $password = NULL) {
+function user_mail_tokens($account = NULL) {
global $base_url;
+
+ // Site-wide tokens
$tokens = array(
- '!username' => $account->name,
- '!site' => variable_get('site_name', 'Drupal'),
- '!login_url' => user_pass_reset_url($account),
- '!uri' => $base_url,
+ '!site_name' => variable_get('site_name', 'Drupal'),
+ '!uri_full' => $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)),
+ '!date' => array('#type' => 'date', 'timestamp' =>time()),
);
- if (!empty($password)) {
- $tokens['!password'] = $password;
+
+ // User dependent values
+ if ($account) {
+ $tokens += array(
+ '!username' => $account->name,
+ '!login_url' => user_pass_reset_url($account),
+ '!mailto' => $account->mail,
+ '!edit_uri' => array('#type' => 'url', 'url' => 'user/'. $account->uid .'/edit', 'options' => array('absolute' => TRUE)),
+ );
}
return $tokens;
}
@@ -3104,50 +3185,57 @@
* 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) {
$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['#mail_id'] = $mail_id;
+ $message['subject'] = _user_mail_text($op .'_subject');
+ $message['body'][] = _user_mail_text($op .'_body');
+ $message['variables'] = $variables;
+ $result = user_mail($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);
+ $message['#mail_id'] = 'user-register-approval-admin';
+ $message['body'][] = text_t("!username has applied for an account.\n\n!edit_uri");
+ user_mail(variable_get('site_mail', ini_get('sendmail_from')), $message);
}
}
+
return $result;
}