? privatemsg_recipient_list/add-rule-button.png
? privatemsg_recipient_list/remove-rule-button.png
? privatemsg_recipient_list/translations
Index: privatemsg.pages.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.pages.inc,v
retrieving revision 1.1
diff -u -p -r1.1 privatemsg.pages.inc
--- privatemsg.pages.inc	2 Dec 2009 20:03:59 -0000	1.1
+++ privatemsg.pages.inc	3 Feb 2010 07:04:53 -0000
@@ -225,6 +225,12 @@ function privatemsg_new(&$form_state, $r
   if (!empty($to)) {
     $recipients_string = implode(', ', $to);
   }
+  if (!$thread_id && !empty($recipients_string)) {
+    drupal_set_title(t('Write new message to %recipient', array('%recipient' => $recipients_string)));
+  }
+  elseif (!$thread_id) {
+    drupal_set_title(t('Write new message'));
+  }
   if (isset($form_state['values'])) {
     if (isset($form_state['values']['recipient'])) {
       $recipients_string = $form_state['values']['recipient'];
@@ -233,12 +239,6 @@ function privatemsg_new(&$form_state, $r
     $subject   = $form_state['values']['subject'];
     $body      = $form_state['values']['body'];
   }
-  if (!$thread_id && !empty($recipients_string)) {
-    drupal_set_title(t('Write new message to %recipient', array('%recipient' => $recipients_string)));
-  }
-  elseif (!$thread_id) {
-    drupal_set_title(t('Write new message'));
-  }
 
   $form = array();
   if (isset($form_state['privatemsg_preview'])) {
Index: privatemsg_recipient_list/privatemsg_recipient_list.css
===================================================================
RCS file: privatemsg_recipient_list/privatemsg_recipient_list.css
diff -N privatemsg_recipient_list/privatemsg_recipient_list.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ privatemsg_recipient_list/privatemsg_recipient_list.css	3 Feb 2010 07:04:54 -0000
@@ -0,0 +1,52 @@
+#privatemsg-recipient-list-settings .sticky-header {
+  /* Stop the remove button overlapping the sticky header */
+  z-index: 10;
+}
+#privatemsg-recipient-list-settings .throbber {
+  margin: 4px 0;
+}
+#add-rule-button, #remove-rule-button {
+  height: 45px;
+  /* Stop the remove button overlapping the sticky header */
+  z-index: 1;
+}
+#add-rule-button label {
+  line-height: 26px;
+  margin-left: 2px;
+}
+#remove-rule-button {
+  width: 100px;
+  position: relative;
+}
+#remove-rule-button label {
+  display: block;
+  clear: both;
+  text-align: center;
+}
+#add-rule-button input,
+#remove-rule-button input {
+  display: inline-block;
+  overflow: hidden;
+  width: 24px;
+  height: 24px;
+  padding: 0;
+  margin: 0;
+  border: 0;
+  text-indent: -9999px;
+  cursor: pointer; /* Hand shaped cursor */
+  cursor: hand; /* for old versions of IE */
+}
+#add-rule-button input {
+  background: transparent url('add-rule-button.png') left top no-repeat;
+}
+#remove-rule-button input {
+  background: transparent url('remove-rule-button.png') left top no-repeat;
+  /* Only way to centre-align remove button without having AHAH throbber
+   * interfere with it */
+  margin-left: 36px;
+}
+
+select#edit-recipient {
+  min-width: 300px;
+}
+
Index: privatemsg_recipient_list/privatemsg_recipient_list.info
===================================================================
RCS file: privatemsg_recipient_list/privatemsg_recipient_list.info
diff -N privatemsg_recipient_list/privatemsg_recipient_list.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ privatemsg_recipient_list/privatemsg_recipient_list.info	3 Feb 2010 07:04:54 -0000
@@ -0,0 +1,7 @@
+; $Id$
+name = Privatemsg Recipient List
+description = Displays a list of users instead of a autocomplete for specific roles.
+package = Mail
+core = 6.x
+dependencies[] = privatemsg
+
Index: privatemsg_recipient_list/privatemsg_recipient_list.module
===================================================================
RCS file: privatemsg_recipient_list/privatemsg_recipient_list.module
diff -N privatemsg_recipient_list/privatemsg_recipient_list.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ privatemsg_recipient_list/privatemsg_recipient_list.module	3 Feb 2010 07:04:59 -0000
@@ -0,0 +1,406 @@
+<?php
+// $Id$
+
+function privatemsg_recipient_list_menu() {
+  $items['admin/settings/messages/list'] = array(
+    'title'            => 'Recipient List rules',
+    'description'      => 'Configure rules for which roles only see a list of recipients.',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('privatemsg_recipient_list_settings'),
+    'access arguments' => array('administer privatemsg settings'),
+    'type'             => MENU_LOCAL_TASK,
+  );
+  $items['messages/recipient_list/js'] = array(
+    'title' => 'Javascript recipient list rules form',
+    'page callback' => 'privatemsg_recipient_list_js',
+    'access arguments' => array('administer privatemsg settings'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function privatemsg_recipient_list_theme() {
+  return array(
+    'privatemsg_recipient_list_rules' => array(
+      'arguments' => array('form' => NULL),
+    ),
+  );
+}
+/**
+ * Implement hook_init().
+ */
+function privatemsg_recipient_list_init() {
+  drupal_add_css(drupal_get_path('module', 'privatemsg_recipient_list') .'/privatemsg_recipient_list.css');
+}
+
+/**
+ * Menu callback for privatemsg list settings.
+ */
+function privatemsg_recipient_list_settings(&$form_state) {
+  // Need to cache form for AHAH, so it can be rebuilt from cache later.
+  $form = array(
+    '#cache' => TRUE,
+  );
+
+  // Container for just the actions, used for AHAH.
+  $form['rules'] = array(
+    '#tree' => TRUE,
+    '#prefix' => '<div id="list-rules">',
+    '#suffix' => '</div>',
+    '#theme' => 'privatemsg_recipient_list_rules',
+  );
+
+  // Should we populate the form with data from $form_state or the database?
+  if (!isset($form_state['privatemsg_recipient_list']['rules'])) {
+    $rules = variable_get('privatemsg_recipient_list_rules', array());
+  }
+  else {
+    $rules = $form_state['privatemsg_recipient_list']['rules'];
+  }
+  // Work through each rule, adding it as a new element in
+  // $form['rules'] ready to be themed later.
+  foreach ($rules as $delta => $details) {
+    $details['delta'] = $delta;
+    $form['rules'][$delta] = _privatemsg_recipient_list_rules_form($details);
+  }
+
+  // The magic AHAH callback button that adds more rows.
+  $form['privatemsg_recipient_list_more'] = array(
+    '#type' => 'submit',
+    '#value' => t('More'),
+    '#weight' => 1,
+    '#prefix' => '<div id="add-rule-button">',
+    '#suffix' => '<label for="edit-privatemsg-recipient-list-more">' . t('Add new rule') . '</label></div>',
+    '#submit' => array('privatemsg_recipient_list_more_submit'),
+    '#ahah' => array(
+      'path' => 'messages/recipient_list/js',
+      'wrapper' => 'list-rules',
+      'method' => 'replace',
+      'effect' => 'fade',
+    ),
+  );
+
+  $form['submit_form'] = array(
+    '#type' => 'submit',
+    '#weight' => 10,
+    '#value' => t('Save configuration'),
+  );
+
+  return $form;
+}
+
+/**
+ * Theme the user actions form.
+ *
+ * @ingroup themeable
+ */
+function theme_privatemsg_recipient_list_rules($form) {
+  $rows = array();
+  $headers = array(
+    t('If the author has the role'),
+    t('Show the users of the following role in a select box'),
+    t('Enabled'),
+    '',
+  );
+  $form_data = element_children($form);
+
+  foreach ($form_data as $key) {
+    // Build the table row.
+    $row = array(
+      'data' => array(
+        array('data' => drupal_render($form[$key]['author'])),
+        array('data' => drupal_render($form[$key]['recipient'])),
+        array('data' => drupal_render($form[$key]['enabled'])),
+        array('data' => drupal_render($form[$key]['remove'])),
+      ),
+    );
+
+    // Add additional attributes to the row, such as a class for this row.
+    if (isset($form[$key]['#attributes'])) {
+      $row = array_merge($row, $form[$key]['#attributes']);
+    }
+    $rows[] = $row;
+  }
+
+  // If there are no rows, output some instructions for the user.
+  if (empty($form_data)) {
+    $rows[] = array(
+      array(
+        'data' => t("No rules have been added. All users see the autocomplete recipient field. To limit which users only see a selectable list, click 'Add new rule'."),
+        'colspan' => '5',
+      ),
+    );
+  }
+
+  $output = theme('table', $headers, $rows);
+  $output .= drupal_render($form);
+
+  return $output;
+}
+
+/**
+ * Builds row of sending, receiving roles and actions that go with them.
+ *
+ * @param $details
+ *   Details of the row: default values and the unique row number (delta).
+ *
+ * @return
+ *   Part of a form with controls for sending, receiving and actions.
+ */
+function _privatemsg_recipient_list_rules_form($details) {
+  $form = array(
+    '#tree' => TRUE,
+  );
+  $delta = $details['delta'];
+  // FALSE by default, or if the user has checked the 'Enabled' check box for
+  // this row.
+  $row_disabled = (isset($details['enabled']) ? !$details['enabled'] : FALSE);
+
+  $form['author'] = array(
+    '#type' => 'select',
+    '#options' => user_roles(TRUE),
+    '#default_value' => (isset($details['author']) ? $details['author'] : DRUPAL_AUTHENTICATED_RID),
+    '#disabled' => $row_disabled,
+  );
+  $form['recipient'] = array(
+    '#type' => 'select',
+    '#options' => user_roles(TRUE),
+    '#default_value' => (isset($details['recipient']) ? $details['recipient'] : DRUPAL_AUTHENTICATED_RID),
+    '#disabled' => $row_disabled,
+  );
+
+  $form['enabled'] = array(
+    '#type' => 'checkbox',
+    '#default_value' => (isset($details['enabled']) ? $details['enabled'] : TRUE),
+  );
+  $form['remove'] = array(
+    '#type' => 'submit',
+    '#submit' => array('privatemsg_recipient_list_remove_submit'),
+    '#value' => t('Remove_' . $delta),
+    '#attributes' => array('class' => 'remove-action'),
+    '#prefix' => '<div id="remove-rule-button">',
+    '#suffix' => '<label for="edit-remove">' . t('Remove rule') . '</label></div>',
+    '#ahah' => array(
+      'path' => 'messages/recipient_list/js',
+      'wrapper' => 'list-rules',
+      'method' => 'replace',
+      'effect' => 'fade',
+    ),
+  );
+
+  return $form;
+}
+
+/**
+ * Submit handler for 'More' button, adds a new action.
+ *
+ * @see privatemsg_recipient_list_remove_submit()
+ */
+function privatemsg_recipient_list_more_submit($form, &$form_state) {
+  unset($form_state['submit_handlers']);
+  form_execute_handlers('submit', $form, $form_state);
+  // Get the submitted actions, then put them into a special area of
+  // the $form_state.
+  $submitted_values = $form_state['values'];
+  // Add an empty action.
+  $submitted_values['rules'][] = array();
+  $form_state['privatemsg_recipient_list'] = $submitted_values;
+  // Rebuild the form by passing our $form_state through the
+  // privatemsg_recipient_list_settings() builder function.
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Submit handler for 'Remove' button, removes an action.
+ *
+ * @see privatemsg_recipient_list_more_submit()
+ */
+function privatemsg_recipient_list_remove_submit($form, &$form_state) {
+  unset($form_state['submit_handlers']);
+  form_execute_handlers('submit', $form, $form_state);
+  $submitted_values = $form_state['values'];
+  // Remove the requested action.
+  $delta = $form_state['clicked_button']['#parents'][1];
+  unset($submitted_values['rules'][$delta]);
+  $form_state['privatemsg_recipient_list'] = $submitted_values;
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Submit handler for admin form.
+ */
+function privatemsg_recipient_list_settings_submit($form, &$form_state) {
+  // We don't want it to submit when we're adding/removing actions.
+  if ($form_state['clicked_button']['#id'] == 'edit-submit-form') {
+    // If the form's 'rules' aren't set, the user has deleted all the
+    // rows in the table, so we save an empty array to stop errors in the form
+    // builder.
+    if (isset($form_state['values']['rules'])) {
+      variable_set('privatemsg_recipient_list_rules', _privatemsg_recipient_list_settings_filter($form_state['values']['rules']));
+    }
+    else {
+      variable_set('privatemsg_recipient_list_rules', array());
+    }
+    drupal_set_message(t('The configuration options have been saved.'));
+  }
+}
+
+/**
+ * Takes an array of settings and filters out anything that's un-needed.
+ * Leaving only settings to be saved.
+ *
+ * @param $settings
+ *   The array of settings to filter.
+ *
+ * @return
+ *   Array of settings, ready to be stored in the database.
+ *
+ * @see privatemsg_recipient_list_settings_submit()
+ */
+function _privatemsg_recipient_list_settings_filter($settings) {
+  // Add-in the names of any settings to be saved into the array below.
+  $save_keys = array('author', 'recipient', 'enabled');
+  $matching = array();
+  // Run through each of the keys we want to save, creating a new array.
+  // It's not possible to simply check for unwanted values and unset() them as
+  // the array is multi-dimensional.
+  foreach ($save_keys as $save_key) {
+    if (isset($settings[$save_key])) {
+      $matching[$save_key] = $settings[$save_key];
+    }
+  }
+  if (count($matching) > 0) {
+    return $matching;
+  }
+  else {
+    return array_map('_privatemsg_recipient_list_settings_filter', $settings);
+  }
+}
+
+/**
+ * Menu callback for AHAH handling.
+ */
+function privatemsg_recipient_list_js() {
+  // See: http://drupal.org/node/331941 for the philosophy of Drupal AHAH.
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, $form_state);
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+  $form['#post'] = $_POST;
+  $form['#redirect'] = FALSE;
+  $form['#programmed'] = FALSE;
+  $form_state['post'] = $_POST;
+  drupal_process_form($form_id, $form, $form_state);
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+  $output_form = $form['rules'];
+  unset($output_form['#prefix'], $output_form['#suffix']); // Prevent duplicate wrappers.
+  $output = theme('status_messages') . drupal_render($output_form);
+
+  // Hack to make behaviours attach to new HTML controls (delete buttons in
+  // this case).
+  $javascript = drupal_add_js(NULL, NULL, 'header');
+  drupal_json(array('status' => TRUE, 'data' => $output));
+}
+
+/**
+ * Add select list to write new form.
+ */
+function privatemsg_recipient_list_form_privatemsg_new_alter(&$form, &$form_state) {
+  if (!isset($form['privatemsg']['thread_id'])) {
+    _privatemsg_recipient_list_replace_autocomplete($form['privatemsg']['recipient']);
+  }
+  if (isset($form['forward']['to'])) {
+    _privatemsg_recipient_list_replace_autocomplete($form['forward']['to']);
+  }
+}
+
+/**
+ * Add select list to dropdown filter.
+ */
+function privatemsg_recipient_list_form_privatemsg_list_alter(&$form, &$form_state) {
+  if (isset($form['filter'])) {
+    _privatemsg_recipient_list_replace_autocomplete($form['filter']['author']);
+  }
+}
+
+/**
+ * Replaces a given autocomplete field with an select list, if configured so.
+ * 
+ * @param $field
+ *   FAPI field array that should be changed if applicable.
+ */
+function _privatemsg_recipient_list_replace_autocomplete(&$field) {
+  global $user;
+  $rules = variable_get('privatemsg_recipient_list_rules', array());
+  // If there are any rules...
+  if (!empty($rules)) {
+    $recipients = _privatemsg_recipient_list_load_recipients($rules, $user);
+
+    // Remove blocked recipients.
+    if (!empty($recipients)) {
+      foreach(module_invoke_all('privatemsg_block_message', $user, $recipients) as $blocked) {
+        unset($recipients[$blocked['uid']]);
+      }
+    }
+    if (!empty($recipients)) {
+      $recipient_list = array();
+      foreach ($recipients as $recipient) {
+        $recipient_list[$recipient->uid] = strip_tags(theme('username', $recipient, array('plain' => TRUE)));
+      }
+    // Change type, add options and enable multiple
+    //var_dump($field);
+      $field['#type'] = 'select';
+      $field['#options'] = $recipient_list;
+      $field['#size'] = 5;
+      $field['#multiple'] = TRUE;
+      $field['#description'] = '';
+
+      if (isset($field['#default_value'])) {
+        $field['#default_value'] = array_keys(reset(_privatemsg_parse_userstring($field['#default_value'])));
+      }
+
+      unset($field['#autocomplete_path']);
+    }
+  }
+}
+
+/**
+ * Load the recipients a user is allowed to sent to, based on the rules.
+ * @param $rules
+ *   Recipient list rules array.
+ */
+function _privatemsg_recipient_list_load_recipients($rules, $account) {
+  $roles = array();
+  $recipients = array();
+  // Get all recipient roles where the current user is member of the author role.
+  foreach ($rules as $rule) {
+    if (isset($account->roles[$rule['author']]) && $rule['enabled']) {
+      $roles[] = $rule['recipient'];
+    }
+  }
+  // If there are any limitations, load those users and display them in a
+  // select list.
+  if (!empty($roles)) {
+    $result = db_query('SELECT u.uid FROM {users} u INNER JOIN {users_roles} ur ON (ur.uid = u.uid) WHERE ur.rid IN (' . db_placeholders($roles) . ')', $roles);
+    while (($rec_uid = db_fetch_object($result)) && $recipient = user_load(array('uid' => $rec_uid->uid))) {
+      $recipients[$recipient->uid] = $recipient;
+    }
+  }
+  return $recipients;
+}
+
+/**
+ * Implements hook_privatemsg_name_lookup().
+ */
+function privatemsg_recipient_list_privatemsg_name_lookup($string) {
+  if ((int)$string > 0) {
+    // This is a possible uid, try to load a matching user.
+    if ($recipient = user_load(array('uid' => $string))) {
+      return $recipient;
+    }
+  }
+}
\ No newline at end of file
