From 9240417271266f2b64c91d910d61214b22101a20 Mon Sep 17 00:00:00 2001
From: Sascha Grossenbacher <saschagros+test@gmail.com>
Date: Sat, 5 Mar 2011 11:57:25 +0100
Subject: [PATCH] Issue #967164 by te-brian, Berdir: Added more fine-grained email notification settings and email notification support for messages sent to roles and other recipient types.

---
 pm_email_notify/pm_email_notify.admin.inc |   47 ++-
 pm_email_notify/pm_email_notify.install   |   67 ++---
 pm_email_notify/pm_email_notify.module    |  259 ++++++++++++++---
 pm_email_notify/pm_email_notify.test      |  455 +++++++++++++++++++++++++++++
 privatemsg.api.php                        |    6 +-
 privatemsg.module                         |   22 ++-
 6 files changed, 759 insertions(+), 97 deletions(-)
 create mode 100644 pm_email_notify/pm_email_notify.test

diff --git a/pm_email_notify/pm_email_notify.admin.inc b/pm_email_notify/pm_email_notify.admin.inc
index 0dd91cf..9e48ba7 100644
--- a/pm_email_notify/pm_email_notify.admin.inc
+++ b/pm_email_notify/pm_email_notify.admin.inc
@@ -16,39 +16,54 @@ function pm_email_notify_admin_settings_form() {
     '#collapsible' => FALSE,
     '#collapsed' => FALSE,
   );
-  $form['pm_email']['pm_email_notify_default'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Notify users of new private messages by default'),
-    '#default_value' => variable_get('pm_email_notify_default', TRUE),
+  $form['pm_email']['privatemsg_setting_email_notify_level'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default e-mail notification level'),
+    '#options' => array(
+      PM_EMAIL_NOTIFY_LEVEL_DISABLED => t('Never send a notification'),
+      PM_EMAIL_NOTIFY_LEVEL_THREAD => t('Only send a notification when a new discussion thread is created'),
+      PM_EMAIL_NOTIFY_LEVEL_UNREAD_ONCE => t("Only send a notification when the user has not already received a notification since he last viewed a discussion thread."),
+      PM_EMAIL_NOTIFY_LEVEL_ALL => t('Always send a notification'),
+    ),
+    '#default_value' => variable_get('privatemsg_setting_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_ALL),
+    '#description' => t('Choose when e-mail notifications will be sent by the system. Users with the appropriate permission may override this setting on the account edit page.'),
     '#weight' => 0,
   );
+  $form['pm_email']['privatemsg_setting_email_notify_only_user'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Only send e-mail notifications to individual users who are addressed directly'),
+    '#default_value' => variable_get('privatemsg_setting_email_only_user', FALSE),
+    '#weight' => 5,
+  );
 
-  $form['pm_email']['pm_email_notify_desc'] = array(
-    '#type' => 'markup',
-    '#value' => t('Customize the email messages sent to users upon receipt of a new private message. <br />Available variables are: !tokens. <br />To translate these messages use the i18nstrings.module and translate them trough the <a href="!translate_url">Translate interface</a> ("Privatemsg Email Notification" text group).', array('!tokens' => _pm_email_notify_token_list(), '!translate_url' => url('admin/build/translate/search'))),
-    '#weight' => 1,
+
+  $form['pm_email']['pm_email_template'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('E-mail customizations'),
+    '#weight' => 15,
+    '#description' => t('Customize the e-mail notification sent to users. Available variables are: !author, !author_uid, !pm_subject, !pm_body, !thread, !site, !login_url, !uri, !uri_brief, !message (URL) and !settings (URL).</p><p>To translate these messages use the i18nstrings.module and translate them trough the <a href="!translate_url">Translate interface</a> ("Privatemsg Email Notification" text group).</p>', array('!translate_url' => url('admin/build/translate/search'))),
   );
 
-  $form['pm_email']['pm_email_notify_from'] = array(
+  $form['pm_email']['pm_email_template']['pm_email_notify_from'] = array(
     '#type' => 'textfield',
     '#title' => t('From e-mail address for notifications'),
     '#default_value' => variable_get('pm_email_notify_from',''),
-    '#weight' => 2,
+    '#weight' => 10,
     '#description' => t('This is the e-mail address that notifications will come from. Leave blank to use the site default.'),
   );
 
-  $form['pm_email']['pm_email_notify_subject'] = array(
+  $form['pm_email']['pm_email_template']['pm_email_notify_subject'] = array(
     '#type' => 'textfield',
-    '#title' => t('Subject of notification messages'),
+    '#title' => t('Subject'),
     '#default_value' => _pm_email_notify_text('subject', NULL, FALSE),
-    '#weight' => 2,
+    '#weight' => 5,
   );
 
-  $form['pm_email']['pm_email_notify_body'] = array(
+  $form['pm_email']['pm_email_template']['pm_email_notify_body'] = array(
     '#type' => 'textarea',
-    '#title' => t('Body of notification messages'),
+    '#title' => t('Body'),
     '#default_value' => _pm_email_notify_text('body', NULL, FALSE),
-    '#weight' => 3,
+    '#weight' => 10,
   );
   return system_settings_form($form);
 }
\ No newline at end of file
diff --git a/pm_email_notify/pm_email_notify.install b/pm_email_notify/pm_email_notify.install
index 408d843..6f34bdb 100644
--- a/pm_email_notify/pm_email_notify.install
+++ b/pm_email_notify/pm_email_notify.install
@@ -2,44 +2,10 @@
 
 /**
  * @file
- * This file takes care of creating/deletig pm_email_notify table used
- * for storing user notification preferences
+ *
+ * This file contains installation and update hooks for pm_email_notify.
  */
 
-function pm_email_notify_schema() {
-  $schema = array();
-
-  $schema['pm_email_notify'] = array(
-    'description' => '{pm_email_notify} holds private message email notification settings for each user ',
-    'fields' => array(
-      'user_id' => array(
-        'description'   => 'ID of the user',
-        'type'          => 'int',
-        'not null'      => TRUE,
-        'unsigned'      => TRUE,
-      ),
-      'email_notify_is_enabled' => array(
-        'description'   => 'Email Notification Status (Enabled=1, Disabled=0)',
-        'type'          => 'int',
-        'not null'      => TRUE,
-        'unsigned'      => TRUE,
-      ),
-    ),
-    'unique keys' => array(
-      'user_id' => array('user_id')
-    ),
-  );
-
-  return $schema;
-}
-
-/**
- * Implements hook_install().
- */
-function pm_email_notify_install() {
-  drupal_install_schema('pm_email_notify');
-}
-
 /**
  * Implements hook_uninstall().
  */
@@ -48,5 +14,32 @@ function pm_email_notify_uninstall() {
   variable_del('pm_email_notify_default');
   variable_del('pm_email_notify_subject');
   variable_del('pm_email_notify_from');
-  drupal_uninstall_schema('pm_email_notify');
+  variable_del('privatemsg_setting_email_notify_level');
+  variable_del('privatemsg_setting_email_notify_only_user');
+}
+
+/**
+ * Convert the old table to privatemsg settings API.
+ */
+function pm_email_notify_update_6201() {
+  $ret = array();
+
+  // Update the site-wide default setting.
+  if (variable_get('pm_email_notify_default', TRUE)) {
+    variable_set('privatemsg_setting_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_ALL);
+  }
+  else {
+    variable_set('privatemsg_setting_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_DISABLED);
+  }
+  variable_del('pm_email_notify_default');
+
+  // Convert user saved settings.
+  $result = db_query('SELECT * FROM {pm_email_notify}');
+  while ($row = db_fetch_object($result)) {
+    privatemsg_set_setting('user', $row->user_id, 'email_notify_level', $row->email_notify_is_enabled ? PM_EMAIL_NOTIFY_LEVEL_DEFAULT : PM_EMAIL_NOTIFY_LEVEL_DISABLED);
+  }
+
+  db_drop_table($ret, 'pm_email_notify');
+
+  return $ret;
 }
diff --git a/pm_email_notify/pm_email_notify.module b/pm_email_notify/pm_email_notify.module
index f7bf2ac..36454fb 100644
--- a/pm_email_notify/pm_email_notify.module
+++ b/pm_email_notify/pm_email_notify.module
@@ -6,6 +6,38 @@
  */
 
 /**
+ * Disable e-mail notifications.
+ */
+define('PM_EMAIL_NOTIFY_LEVEL_DISABLED', 0);
+
+/**
+ * Enable e-mail notifications only for new threads.
+ */
+define('PM_EMAIL_NOTIFY_LEVEL_THREAD', 4);
+
+/**
+ * Enable e-mail notifications only once until a user visits a threads.
+ */
+define('PM_EMAIL_NOTIFY_LEVEL_UNREAD_ONCE', 8);
+
+/**
+ * Enable e-mail notifications for all messages.
+ */
+define('PM_EMAIL_NOTIFY_LEVEL_ALL', 12);
+
+/**
+ * Enable e-mail notifications and use the global default.
+ */
+define('PM_EMAIL_NOTIFY_LEVEL_DEFAULT', 16);
+
+/**
+ * Implements hook_perm().
+ */
+function pm_email_notify_perm() {
+  return array('set privatemsg e-mail notification level', 'change privatemsg e-mail notification for indirect messages');
+}
+
+/**
  * Implements hook_menu().
  */
 function pm_email_notify_menu() {
@@ -23,46 +55,172 @@ function pm_email_notify_menu() {
 }
 
 /**
- * Retrieve notification setting of a user.
+ * Retrieve notification level of a user.
+ *
+ * This function retrieves user's pm notification level from database,
+ * if user preference doesn't exist - it uses default value instead.
+ *
+ * @param $uid
+ *   User ID.
+ *
+ * @return
+ *   Returns a PM_EMAIL_NOTIFY_LEVEL_* constant indicating the notification
+ *   level of the requested user.
+ */
+function _pm_email_notify_user_level($uid = NULL) {
+  // Either check the setting for this user or the global default.
+  if ($uid) {
+    $keys = array(
+      'user' => array($uid),
+      'global' => array(0),
+    );
+  } else {
+    $keys = array(
+      'global' => array(0),
+    );
+  }
+
+  $value = privatemsg_get_setting('email_notify_level', $keys);
+  if ($value == PM_EMAIL_NOTIFY_LEVEL_DEFAULT) {
+    // Explicitly get the global setting by removing the user key. The global
+    // was already loaded and is kept in the static cache because of the above
+    // query.
+    unset($keys['user']);
+    $value = privatemsg_get_setting('email_notify_level', $keys);
+  }
+
+  // Return the default value if not specified.
+  if ($value === NULL) {
+    $value = PM_EMAIL_NOTIFY_LEVEL_ALL;
+  }
+  return $value;
+}
+
+/**
+ * Check if a user should only be notified when addressed directly.
+ *
+ * @param $uid
+ *   User ID.
+ *
+ * @param
+ *   TRUE if notifications should only be sent for directly addressed
+ *   recipients.
+ */
+function _pm_email_notify_only_user($uid) {
+  // Either check the setting for this user or the global default.
+  $keys = array(
+    'user' => array($uid),
+    'global' => array(0),
+  );
+  $value = privatemsg_get_setting('email_notify_only_user', $keys);
+  return $value;
+}
+
+/**
+ * Retrieve notification setting of a user and check if they should receive
+ * an e-mail notification for a message.
  *
- * This function retrieves user's pm notification preference from database,
- * if user preference doesn't exist - it uses default value instead
+ * Note: This function tries to return as quickly as possible, to avoid extra
+ * processing in batch executions.
  *
  * @param $uid
  *   User uid
+ * @param $message
+ *   Message.
  */
-function _pm_email_notify_is_enabled($uid) {
+function _pm_email_notify_send_check($uid, $message) {
   static $notifications = array();
-  // Cache the result set in case this method is executed in batched operation which will perform many unnecessary repeated selects for the same user
-  if ( !isset($notifications[$uid]) ) {
-    $mail_notification = db_result(db_query('SELECT email_notify_is_enabled FROM {pm_email_notify} WHERE user_id = %d', $uid));
-    if ($mail_notification === FALSE) {
-      // db_result() returns FALSE if result was not found.
-      $mail_notification = variable_get('pm_email_notify_default', TRUE);
+
+  $mid = $message['mid'];
+  $thread_id = $message['thread_id'];
+  $level = _pm_email_notify_user_level($uid);
+
+  // If the user has notifications disabled, we can skip the rest.
+  if ($level == PM_EMAIL_NOTIFY_LEVEL_DISABLED) {
+    return FALSE;
+  }
+
+  // If the user has all notifications enabled, we can skip the rest.
+  if ($level == PM_EMAIL_NOTIFY_LEVEL_ALL) {
+    return TRUE;
+  }
+
+  // Cache the result set in case this method is executed in batched operation
+  // which will perform many unnecessary repeated processing.
+  if (!isset($notifications[$uid][$mid])) {
+    // Prime the setting to false.
+    $notifications[$uid][$mid] = FALSE;
+
+    if ($level == PM_EMAIL_NOTIFY_LEVEL_THREAD) {
+      // Is this the origin of a thread?
+      $notifications[$uid][$mid] = ($mid == $thread_id);
+    }
+    elseif ($level == PM_EMAIL_NOTIFY_LEVEL_UNREAD_ONCE) {
+      // If this is the first message of a thread, always send a notification.
+      if ($mid == $thread_id) {
+        $notifications[$uid][$mid] = TRUE;
+      } else {
+        // Check if this user has more than a single unread message
+        // in that thread. If yes, they already got a notification.
+        // They always have at least one unread message because they just
+        // received one.
+        $unread_count = db_result(db_query("SELECT COUNT(*) FROM {pm_index} WHERE thread_id = %d AND is_new = 1 AND recipient = %d AND type IN ('user', 'hidden')", $thread_id, $uid));
+        $notifications[$uid][$mid] = $unread_count == 1;
+      }
     }
-    $notifications[$uid] = $mail_notification;
   }
-  return $notifications[$uid];
+
+  return $notifications[$uid][$mid];
 }
 
 /**
  * Implements hook_privatemsg_message_insert().
  */
-function pm_email_notify_privatemsg_message_insert($message) {
-  foreach ($message['recipients'] as $recipient) {
-    // check if recipient enabled email notifications
-    if (isset($recipient->uid) && _pm_email_notify_is_enabled($recipient->uid)) {
-      // send them a new pm notification email if they did
-      $params['recipient'] = $recipient;
-      $params['message'] = $message;
-      $from = variable_get('pm_email_notify_from', '');
-      drupal_mail('pm_email_notify', 'notice', $recipient->mail, user_preferred_language($recipient), $params, !empty($from) ? $form : NULL);
+ function pm_email_notify_privatemsg_message_insert($message) {
+   foreach ($message['recipients'] as $recipient) {
+    pm_email_notify_send_mail($recipient, $message);
+  }
+}
+
+/**
+ * Implements hook_privatemsg_message_recipient_changed().
+ *
+ * Notifies users who were added to a message about new Private Messages
+ * via Email.
+ */
+function pm_email_notify_privatemsg_message_recipient_changed($mid, $thread_id, $recipient_id, $type, $added) {
+  $types = array('user');
+
+  // Only send mail if the recipient was added.
+  if ($added) {
+    if ($message = privatemsg_message_load($mid)) {
+      // Check if we should send an email to 'hidden' recipients.
+      if (!_pm_email_notify_only_user($recipient_id)) {
+        $types[] = 'hidden';
+      }
+      if (in_array($type, $types) && _pm_email_notify_send_check($recipient_id, $message) && ($recipient = privatemsg_user_load($recipient_id))) {
+        pm_email_notify_send_mail($recipient, $message);
+      }
     }
   }
 }
 
 /**
- * Implements hook_mail().
+ * Send a pm notification email to a recipient.
+ */
+function pm_email_notify_send_mail($recipient, $message) {
+  // check if recipient enabled email notifications
+  if (isset($recipient->uid) && !empty($recipient->mail) && _pm_email_notify_send_check($recipient->uid, $message)) {
+    // send them a new pm notification email if they did
+    $params['recipient'] = $recipient;
+    $params['message'] = $message;
+    $from = variable_get('pm_email_notify_from', '');
+    drupal_mail('pm_email_notify', 'notice', $recipient->mail, user_preferred_language($recipient), $params, !empty($from) ? $from : NULL);
+  }
+}
+
+/**
+* Implements hook_mail().
  */
 function pm_email_notify_mail($key, &$message, $params) {
   $language = $message['language'];
@@ -179,32 +337,57 @@ function pm_email_notify_user($op, &$edit, &$account, $category = NULL) {
   switch ($op) {
     case 'form':
       if ($category == 'account' && privatemsg_user_access('read privatemsg', $account)) {
-        $form['privatemsg']['pm_send_notifications'] = array(
+        if (privatemsg_user_access('set privatemsg e-mail notification level')) {
+          $form['privatemsg']['pm_email_notify_level'] = array(
+            '#type' => 'radios',
+            '#title' => t('Send me an e-mail notification...'),
+            '#options' => array(
+              PM_EMAIL_NOTIFY_LEVEL_DISABLED => t('Never.'),
+              PM_EMAIL_NOTIFY_LEVEL_THREAD => t('Only for a new conversation'),
+              PM_EMAIL_NOTIFY_LEVEL_UNREAD_ONCE => t("Only once for a conversation until I've read the messages"),
+              PM_EMAIL_NOTIFY_LEVEL_ALL => t('Every time I receive a message'),
+            ),
+            '#default_value' => _pm_email_notify_user_level($account->uid),
+          );
+        }
+        else {
+          // If the user does not have permissions to customize the notification
+          // level, allow him to opt out of email notifications if they are not
+          // disabled by default.
+          $is_enabled = _pm_email_notify_user_level();
+          $form['privatemsg']['pm_email_notify_level'] = array(
+            '#type' => 'checkbox',
+            '#title' => t('Receive email notification for incoming private messages'),
+            '#default_value' => $is_enabled ? PM_EMAIL_NOTIFY_LEVEL_DEFAULT : PM_EMAIL_NOTIFY_LEVEL_DISABLED,
+            '#access' => $is_enabled,
+          );
+        }
+        $form['privatemsg']['pm_email_only_user'] = array(
           '#type' => 'checkbox',
-          '#title' => t('Receive email notification for incoming private messages'),
-          '#default_value' => _pm_email_notify_is_enabled($account->uid),
+          '#title' => t("Don't send me e-mail notifications for mass messages."),
+          '#default_value' => _pm_email_notify_only_user($account->uid),
+          '#access' => privatemsg_user_access('change privatemsg e-mail notification for indirect messages'),
         );
       }
       return $form;
 
     case 'submit':
-      if (isset($edit['pm_send_notifications']) && privatemsg_user_access('read privatemsg', $account)) {
-        $pm_email_enabled = $edit['pm_send_notifications'];
-        unset($edit['pm_send_notifications']);
-        // Update database entry with user preference.
-        $exists = db_result(db_query("SELECT 1 FROM {pm_email_notify} WHERE user_id = %d", $account->uid));
-        if ($exists) {
-          // If there is an existing entry, update.
-          db_query("UPDATE {pm_email_notify} SET email_notify_is_enabled = %d WHERE user_id = %d", $pm_email_enabled, $account->uid);
-        }
-        else {
-          // If not, create a new one.
-          db_query("INSERT INTO {pm_email_notify} (email_notify_is_enabled, user_id) VALUES (%d, %d)", $pm_email_enabled, $account->uid);
-        }
+      if (isset($edit['pm_email_notify_level'])) {
+        privatemsg_set_setting('user', $account->uid, 'email_notify_level', $edit['pm_email_notify_level']);
+        unset($edit['pm_email_notify_level']);
+      }
+
+
+      if (isset($edit['pm_email_only_user'])) {
+        privatemsg_set_setting('user', $account->uid, 'email_notify_only_user', $edit['pm_email_only_user']);
+        unset($edit['pm_email_only_user']);
       }
       break;
 
     case 'delete':
+      privatemsg_del_setting('user', $account->uid, 'email_notify_level');
+      privatemsg_del_setting('user', $account->uid, 'email_notify_only_user');
+
       db_query("DELETE FROM {pm_email_notify} WHERE user_id = %d", $account->uid);
       break;
   }
diff --git a/pm_email_notify/pm_email_notify.test b/pm_email_notify/pm_email_notify.test
new file mode 100644
index 0000000..6493015
--- /dev/null
+++ b/pm_email_notify/pm_email_notify.test
@@ -0,0 +1,455 @@
+<?php
+/**
+ * @file
+ * Test file for pm_email_notify.module
+ */
+
+class PrivatemsgEMailNotifyTestCase extends DrupalWebTestCase {
+
+  protected $handledMails = 0;
+
+  /**
+   * Implements getInfo().
+   */
+  function getInfo() {
+    return array
+    (
+      'name' => t('Privatemsg E-Mail notifications.'),
+      'description' => t('Test sending e-mail notifications according to the set configuration.'),
+      'group' => t('Privatemsg'),
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp('privatemsg', 'pm_email_notify', 'privatemsg_roles');
+
+    $this->author = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'write privatemsg to all roles'));
+    $this->recipient = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'set privatemsg e-mail notification level', 'change privatemsg e-mail notification for indirect messages'));
+    $this->recipient2 = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
+    $this->admin = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'administer privatemsg settings'));
+    $this->handledMails = 0;
+  }
+
+  /**
+   * Test Always configuration.
+   */
+  function testAlwaysConfiguration() {
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+    $this->verifyMails(array($this->recipient, $this->recipient2), $this->author, $message);
+
+    // Reply to the existing message.
+    $reply = array(
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $message['body'] = $reply['body'];
+
+    $this->verifyMails(array($this->recipient, $this->recipient2), $this->author, $message);
+
+    // Send a message to all users.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array(user_load(1), $this->author, $this->recipient, $this->recipient2, $this->admin);
+    $this->verifyMails($recipients, $this->author, $message);
+
+    // Verify that the user with the necessary permission chan choose the exact
+    // notification level while the other can only opt out.
+    $this->drupalLogin($this->recipient);
+    $this->drupalGet('user/' . $this->recipient->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertFieldByName('pm_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_ALL, t('Notification level setting has the correct default value'));
+
+    $edit = array(
+      'pm_email_notify_level' => PM_EMAIL_NOTIFY_LEVEL_DISABLED,
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Send another message, only recipient two should get a notification.
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+    $this->verifyMails(array($this->recipient2), $this->author, $message);
+
+    // Opt out of notifications with the other user.
+    $this->drupalLogin($this->recipient2);
+    $this->drupalGet('user/' . $this->recipient2->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertFieldChecked('edit-pm-email-notify-level', PM_EMAIL_NOTIFY_LEVEL_DEFAULT, t('Notification level is set to default.'));
+
+    $edit = array(
+      'pm_email_notify_level' => FALSE,
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Send another message, only recipient two should get a notification.
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $this->getNewMails(0);
+  }
+
+  /**
+   * Test Always configuration.
+   */
+  function testOnlyUserConfiguration() {
+    $this->drupalLogin($this->author);
+
+    // Send a message to all users.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array(user_load(1), $this->author, $this->recipient, $this->recipient2, $this->admin);
+    $this->verifyMails($recipients, $this->author, $message);
+
+    // Enable only user.
+    $this->drupalLogin($this->admin);
+    $edit = array(
+      'privatemsg_setting_email_notify_only_user' => TRUE,
+    );
+    $this->drupalPost('admin/settings/messages/notify', $edit, t('Save configuration'));
+    $this->drupalLogin($this->author);
+
+    // No notifications should be sent now.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $this->getNewMails(0);
+
+    // Verify that only the user with the necessary permission can change
+    // the default setting.
+    $this->drupalLogin($this->recipient);
+    $this->drupalGet('user/' . $this->recipient->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertFieldChecked('edit-pm-email-only-user', t('Only user option has the correct default value'));
+    //$this->assertFieldByName('pm_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_ALL, t('Notification level setting has the correct default value'));
+
+    $edit = array(
+      'pm_email_only_user' => FALSE,
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+
+    $this->drupalLogin($this->recipient2);
+    $this->drupalGet('user/' . $this->recipient2->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertNoFieldById('edit-pm-email-only-user', NULL, t('User is not allowed to customize noly user option.'));
+    //$this->assertNoText(t('Private messages'), t('Private messages fieldset is not displayed when there are no fields within it.'));
+
+    $this->drupalLogin($this->author);
+
+    // Send another message to all users, only $this->recipient should receive
+    // a notification.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array($this->recipient);
+    $this->verifyMails($recipients, $this->author, $message);
+  }
+
+  /**
+   * Test Always configuration.
+   */
+  function testThreadConfiguration() {
+    // Configure to only send notifications for new messages.
+    $this->drupalLogin($this->admin);
+    $edit = array(
+      'privatemsg_setting_email_notify_level' => PM_EMAIL_NOTIFY_LEVEL_THREAD,
+    );
+    $this->drupalPost('admin/settings/messages/notify', $edit, t('Save configuration'));
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+    $this->verifyMails(array($this->recipient, $this->recipient2), $this->author, $message);
+
+    // Reply to the existing message.
+    $reply = array(
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $message['body'] = $reply['body'];
+    $this->getNewMails(0);
+
+    // Send a message to all users.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array(user_load(1), $this->author, $this->recipient, $this->recipient2, $this->admin);
+    $this->verifyMails($recipients, $this->author, $message);
+  }
+
+  /**
+   * Test Always configuration.
+   */
+  function testUnreadConfiguration() {
+    // Configure to only send notifications for new messages.
+    $this->drupalLogin($this->admin);
+    $edit = array(
+      'privatemsg_setting_email_notify_level' => PM_EMAIL_NOTIFY_LEVEL_UNREAD_ONCE,
+    );
+    $this->drupalPost('admin/settings/messages/notify', $edit, t('Save configuration'));
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+    $this->verifyMails(array($this->recipient, $this->recipient2), $this->author, $message);
+
+    // Reply to the existing message.
+    $reply = array(
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $message['body'] = $reply['body'];
+    $this->getNewMails(0);
+
+    // Visit the thread with the first recipient.
+    $this->drupalLogin($this->recipient);
+    $this->drupalGet('messages');
+    $this->clickLink($message['subject']);
+
+    // Back to author, send another message.
+    $this->drupalLogin($this->author);
+    $this->drupalGet('messages/view/' . $message['thread_id']);
+    $reply = array(
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $message['body'] = $reply['body'];
+    $this->verifyMails(array($this->recipient), $this->author, $message);
+
+    // Another reply, again, no notifications.
+    $reply = array(
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost(NULL, $reply, t('Send message'));
+    $message['body'] = $reply['body'];
+    $this->getNewMails(0);
+
+    // Send a message to all users.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array(user_load(1), $this->author, $this->recipient, $this->recipient2, $this->admin);
+    $this->verifyMails($recipients, $this->author, $message);
+  }
+
+  /**
+   * Test that no mails are sent when
+   */
+  function testNeverConfiguration() {
+    // Disable notifications by default.
+    $this->drupalLogin($this->admin);
+    $edit = array(
+      'privatemsg_setting_email_notify_level' => PM_EMAIL_NOTIFY_LEVEL_DISABLED,
+    );
+    $this->drupalPost('admin/settings/messages/notify', $edit, t('Save configuration'));
+
+    $this->drupalLogin($this->author);
+
+    // Send a new message.
+    $message = array(
+      'recipient' => $this->recipient->name . ', ' . $this->recipient2->name,
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $this->getNewMails(0);
+
+    // Send a message to all users.
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $this->getNewMails(0);
+
+    // Verify that only the user with the necessary permission enable email
+    // notifications.
+    $this->drupalLogin($this->recipient);
+    $this->drupalGet('user/' . $this->recipient->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertFieldByName('pm_email_notify_level', PM_EMAIL_NOTIFY_LEVEL_ALL, t('Notification level setting has the correct default value'));
+
+    $edit = array(
+      'pm_email_notify_level' => PM_EMAIL_NOTIFY_LEVEL_ALL,
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    $this->drupalLogin($this->recipient2);
+    $this->drupalGet('user/' . $this->recipient2->uid . '/edit');
+
+    // Verify correct defaults.
+    $this->assertNoFieldById('edit-pm-email-notify-level', NULL, t('User is not allowed change email notification setting.'));
+    $this->assertNoText(t('Private messages'), t('Private messages fieldset is not displayed when there are no fields within it.'));
+
+    // Send a message to all users, only recipient should receive an email.
+    $this->drupalLogin($this->author);
+    $message = array(
+      'recipient' => 'authenticated user',
+      'subject' => $this->randomName(),
+      'body' => $this->randomName(20),
+    );
+    $this->drupalPost('messages/new', $message, t('Send message'));
+    $message['thread_id'] = $this->getCurrentThreadId();
+
+    $recipients = array($this->recipient);
+    $this->verifyMails($recipients, $this->author, $message);
+  }
+
+  /**
+   * Extracts the current thread_id from the URl.
+   *
+   * @return
+   *   Thread id of the current page.
+   */
+  function getCurrentThreadId() {
+    preg_match('|messages\/view\/([0-9]+)|', $this->getURL(), $matches);
+    return $matches[1];
+  }
+
+  /**
+   * Return new mails.
+   *
+   * Returns all mails which were sent since the last call to this function.
+   *
+   * @param $expectedNumber
+   *   Number of new mails that should be there.
+   */
+  function getNewMails($expectedNumber) {
+    // Load all mails of the current test.
+    $mails = $this->drupalGetMails();
+
+    // Slice of the new ones.
+    $mails_slice = array_slice($mails, $this->handledMails);
+
+    // Update amount of already handled mails.
+    $this->handledMails = count($mails);
+
+    if ($expectedNumber !== FALSE) {
+      $this->assertEqual(count($mails_slice), $expectedNumber, t('@expected notification mails were sent.', array('@expected' => $expectedNumber)));
+    }
+    return $mails_slice;
+  }
+
+  /**
+   * Verify multiple notification mails
+   *
+   * @param $recipients
+   *   Array of recipients in the order in which they received their mail.
+   * @param $author
+   *   Author user object.
+   * @param $message
+   *   Message array, containing the keys thread_id, body and subject.
+   */
+  function verifyMails($recipients, $author, $message) {
+    $mails = $this->getNewMails(count($recipients));
+    foreach ($recipients as $id => $recipient) {
+      $this->verifyMail($mails[$id], $recipient, $author, $message);
+    }
+  }
+
+  /**
+   * Verify a notification mail.
+   *
+   * @param $mail
+   *   Mail array as returned by DrupalWebTestCase::drupalGetMails().
+   * @param $recipient
+   *   Recipient user object.
+   * @param $author
+   *   Author user object.
+   * @param $message
+   *   Message array, containing the keys thread_id, body and subject.
+   */
+  function verifyMail($mail, $recipient, $author, $message) {
+    $this->assertEqual($mail['id'], 'pm_email_notify_notice', t('Correct notification id.'));
+    $this->assertEqual($mail['to'], $recipient->mail, t('Correct recipient e-mail address.'));
+    $params = array(
+      '!author_uid' => $author->uid,
+      '!author' => privatemsg_recipient_format($author, array('plain' => TRUE)),
+      '!pm_subject' => trim(drupal_html_to_text(check_plain($message['subject']))),
+      '!pm_body' => trim(drupal_html_to_text(check_markup($message['body'], NULL, FALSE))),
+      '!thread' => $message['thread_id'],
+      '!user_uid' => $recipient->uid,
+      '!message' => url(privatemsg_get_dynamic_url_prefix($recipient->uid) . '/view/' . $message['thread_id'], array('absolute' => TRUE)),
+      '!settings' => url('user/' . $recipient->uid . '/edit', array('absolute' => TRUE)),
+      '!site' => variable_get('site_name', 'Drupal'),
+      '!username' => privatemsg_recipient_format($recipient, array('plain' => TRUE)),
+    );
+    $subject = strtr(_pm_email_notify_text('subject'), $params);
+    $body = drupal_wrap_mail(strtr(_pm_email_notify_text('body'), $params));
+    $this->assertEqual($mail['subject'], $subject, t('Correct subject'));
+    $this->assertEqual($mail['body'], $body, t('Correct body'));
+  }
+}
\ No newline at end of file
diff --git a/privatemsg.api.php b/privatemsg.api.php
index f22c4a4..3bb1f8d 100644
--- a/privatemsg.api.php
+++ b/privatemsg.api.php
@@ -370,14 +370,14 @@ function hook_privatemsg_message_insert($message) {
  *   Id of the message.
  * @param $thread_id
  *   Id of the thread the message belongs to.
- * @param $recipient
+ * @param $recipient_id
  *   Recipient id, a user id if type is user or hidden.
  * @param $type
  *   Type of the recipient.
  * @param $added
  *   TRUE if the recipient is added, FALSE if he is removed.
  */
-function hook_privatemsg_message_recipient_changed($mid, $thread_id, $recipient, $type, $added) {
+function hook_privatemsg_message_recipient_changed($mid, $thread_id, $recipient_id, $type, $added) {
   if ($added && ($type == 'user' || $type == 'hidden')) {
     privatemsg_filter_add_tags(array($thread_id), variable_get('privatemsg_filter_inbox_tag', ''), (object)array('uid' => $recipient));
   }
@@ -738,4 +738,4 @@ function hook_privatemsg_header_info() {
  */
 function hook_privatemsg_header_info_alter(&$headers) {
 
-}
\ No newline at end of file
+}
diff --git a/privatemsg.module b/privatemsg.module
index 440ba10..e83152e 100644
--- a/privatemsg.module
+++ b/privatemsg.module
@@ -1366,9 +1366,20 @@ function privatemsg_user($op, &$edit, &$account, $category = NULL) {
  * Hides the settings fieldset if there are no options to be displayed.
  */
 function privatemsg_account_fieldset_remove_if_empty($element) {
+  // If there are no children elements, deny access.
   if (count(element_children($element)) == 0) {
     $element['#access'] = FALSE;
   }
+  else {
+    // If there are elements, check if at least one of them is visible. Deny
+    // access.
+    foreach (element_children($element) as $key) {
+      if ($element[$key]['#type'] != 'value' && !empty($element[$key]['#access'])) {
+        return $element;
+      }
+    }
+    $element['#access'] = FALSE;
+  }
   return $element;
 }
 
@@ -2927,19 +2938,24 @@ function privatemsg_get_setting($setting, $ids = NULL) {
     while ($row = db_fetch_object($result)) {
       $cache[$setting][$row->type][$row->id] = $row->value;
     }
+
+    // If there is no global default in the database, try to get one with
+    // variable_get().
+    if ($cache[$setting]['global'][0] === FALSE) {
+      $cache[$setting]['global'][0] = variable_get('privatemsg_setting_' . $setting, FALSE);
+    }
   }
 
   // Now, go over all cached settings and return the first match.
   foreach ($ids as $type => $type_ids) {
     foreach ($type_ids as $type_id) {
-      if (isset($cache[$setting][$type][$type_id]) && $cache[$setting][$type][$type_id] !== FALSE && $cache[$setting][$type][$type_id] >= 0) {
+      if (isset($cache[$setting][$type][$type_id]) && $cache[$setting][$type][$type_id] !== FALSE) {
         return $cache[$setting][$type][$type_id];
       }
     }
   }
 
-  // Nothing matched, return default.
-  return 0;
+  // Nothing matched, don't return anything.
 }
 
 function privatemsg_set_setting($type, $id, $setting, $value) {
-- 
1.7.4.1

