'Mailing lists', 'access arguments' => array('access content'), 'page callback' => 'drupal_get_form', 'page arguments' => array('lists_subscribe_form'), 'type' => MENU_SUGGESTED_ITEM ); return $items; } // == Mailing list functionality =============================================== /** * Internal helper function to return a list of mailing lists. */ function _lists_get_lists() { $lists = array( 'support' => array( 'name' => 'Support', 'description' => 'A list for support questions.', 'mailto' => 'support-request@drupal.org', ), 'development' => array( 'name' => 'Development', 'description' => 'A list for Drupal developers.', 'mailto' => 'development-request@drupal.org', ), 'themes' => array( 'name' => 'Themes', 'description' => 'A list for Drupal theme developers/designers.', 'mailto' => 'themes-request@drupal.org', ), 'translations' => array( 'name' => 'Translations', 'description' => 'A list for Drupal UI translators.', 'mailto' => 'translations-request@drupal.org', ), 'consulting' => array( 'name' => 'Consulting', 'description' => 'A mailing list for Drupal consultants and Drupal service/hosting providers.', 'mailto' => 'consulting-request@drupal.org', ), 'drupal-cvs' => array( 'name' => 'CVS commits', 'description' => 'A list with all CVS commit messages.', 'mailto' => 'drupal-cvs-request@drupal.org', ), 'cvs-applications' => array( 'name' => 'CVS applications', 'description' => 'A list of all applications for an account in the Drupal contributions CVS repository.', 'mailto' => 'cvs-applications-request@drupal.org', 'private' => TRUE, ), 'webmasters' => array( 'name' => 'Webmasters', 'description' => 'A list for drupal.org webmasters (e.g. settings and content on the drupal.org website, user management, removing spam, etc.).', 'mailto' => 'webmasters-request@drupal.org', 'private' => TRUE, ), 'infrastructure' => array( 'name' => 'Infrastructure', 'description' => 'A list for drupal.org infrastructure maintainers (e.g. drupal.org hardware and server configuration, the CVS repository, mailing lists, etc).', 'mailto' => 'infrastructure-request@drupal.org', 'private' => TRUE, ), 'drupal-con' => array( 'name' => 'DrupalCON', 'description' => 'A list for the organization of Drupal conferences and events.', 'mailto' => 'drupal-con-request@drupal.org', 'private' => TRUE, ), ); return $lists; } /** * Mailing list form builder function. * * @see lists_subscribe_form_validate() * @see lists_subscribe_form_submit() */ function lists_subscribe_form() { global $user; $lists = _lists_get_lists(); foreach ($lists as $list => $info) { $links = array(); if (isset($info['private']) && $info['private']) { $links[] = "view archive (members only)"; } else { $links[] = "view archive"; } $output .= '

'. $info['name'] ."

\n"; if (!$info['disabled']) { $links[] = "mailman page"; } else { $output .= "This list has been disabled.\n"; } $output .= '

'. $info['description'] ."

\n"; $output .= '

'. implode(' . ', $links) ."

\n"; } $output .= '

Subscribe

'; $form['intro'] = array( '#type' => 'markup', '#value' => $output, ); $form['mail'] = array( '#type' => 'textfield', '#title' => t('E-mail address'), '#default_value' => ($user->uid ? $user->mail : ''), '#size' => 50, '#maxlength' => 255, '#required' => TRUE, ); $form['item'] = array( '#type' => 'item', '#title' => t('Mailing lists'), '#default_value' => '', ); foreach ($lists as $list => $info) { if (!$info['disabled']) { $form[$list] = array( '#type' => 'checkbox', '#title' => $list, ); } } $form['subscribe'] = array( '#type' => 'submit', '#value' => t('Subscribe'), ); return $form; } /** * Validate form submission. * * @see lists_subscribe_form() */ function lists_subscribe_form_validate($form, &$form_state) { if (!valid_email_address($form_state['values']['mail'])) { form_set_error('mail', t('Please enter a valid e-mail address.')); } } /** * Submission handler for the subscription form. * * @see lists_subscribe_form() */ function lists_subscribe_form_submit($form, &$form_state) { $sent = FALSE; $mail = $form_state['values']['mail']; if ($mail) { foreach (_lists_get_lists() as $list => $info) { if ($form_state['values'][$list]) { $sent = TRUE; $headers = "From: $mail\nReturn-path: $mail\nError-to: $mail"; $result[] = mail($info['mailto'], "subscribe address=$mail", 'subscribe to mailing list', $headers); } } } if (!$sent) { drupal_set_message(t('You did not fill in the form properly.'), 'error'); } else { drupal_set_message(t('You will receive confirmation emails for your subscriptions. Please read them carefully and follow the directions.')); } } // == Newsletters ============================================================== /** * Static definition of all mailman managed Drupal mailing lists. */ function _lists_get_mailman_lists() { // passwords need to be set in variable table in order to be able to // administer these lists. $lists = array( 'drupal' => array( 'name' => 'Drupal newsletter', 'description' => 'A sometimes-monthly mailing about all things Drupal.', 'allow_sub' => TRUE, 'mail' => variable_get('lists_drupal_mail', ''), 'pass' => variable_get('lists_drupal_pass', ''), 'url' => variable_get('lists_drupal_url', ''), ), 'security' => array( 'name' => 'Security announcements', 'description' => 'A low volume mailing list where all security issues affecting Drupal and Drupal contributed modules are publically announced.', 'allow_sub' => TRUE, 'mail' => variable_get('lists_security_mail', ''), 'pass' => variable_get('lists_security_pass', ''), 'url' => variable_get('lists_security_url', ''), ), 'maintainer' => array( 'name' => 'Maintainer news', 'description' => 'This list is automatically enabled for users with a CVS account and can not be manually subscribed or unsubscribed.', 'allow_sub' => FALSE, 'maintainer_auto' => TRUE, 'mail' => variable_get('lists_maintainer_mail', ''), 'pass' => variable_get('lists_maintainer_pass', ''), 'url' => variable_get('lists_maintainer_url', ''), ), ); return $lists; } /** * List of all forums that are mailman mailing lists. */ function _lists_get_forum_tids() { return variable_get('lists_forum_tids', array()); } /** * Implementation of hook_perm(). */ function lists_perm() { return array('post to newsletter'); } /** * Implementation of hook_user(). * * We need to hack this in, since user module does try to have a tight control * of management of these menu items, so we need to get it created through * this hook and then altered by hook_menu_alter(). Drupal 7 should do better. */ function lists_user($op, &$edit, &$account, $category = NULL) { switch ($op) { case 'categories': return array(array('name' => 'newsletter', 'title' => t('My newsletters'), 'weight' => 10)); } } /** * Implementation of hook_menu_alter(). * * See why we need this hack in lists_user(). */ function lists_menu_alter(&$callbacks) { $callbacks['user/%user_category/edit/newsletter']['page callback'] = 'drupal_get_form'; $callbacks['user/%user_category/edit/newsletter']['page arguments'] = array('lists_mailman_user_form', 1); } /** * Per-use mailman subscription form. * * @ingroup forms * @see lists_mailman_user_form_submit() */ function lists_mailman_user_form(&$form_state, $account = FALSE) { $form = array(); $form['newsletter'] = array( '#type' => 'fieldset', '#title' => t('Newsletter subscriptions'), '#description' => t('Select the newsletter(s) which you want to subcribe or unsubcribe.'), '#collapsible' => FALSE, ); $lists = _lists_get_mailman_lists(); foreach ($lists as $list => $info) { $default_value = _lists_mailman_user_subscribed($info, $account); $form['newsletter'][$list] = array( '#type' => 'checkbox', '#title' => $info['name'], '#description' => $info['description'], '#disabled' => !$info['allow_sub'], '#default_value' => $default_value, ); } $form['uid'] = array( '#type' => 'hidden', '#value' => $account->uid, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), ); return $form; } /** * Subscribe/unsubscribe from mailman mailing lists. */ function lists_mailman_user_form_submit($form, &$form_state) { $account = user_load(array('uid' => $form_state['values']['uid'])); $lists = _lists_get_mailman_lists(); foreach ($lists as $list => $data) { $old = (int)$form['newsletter'][$list]['#default_value']; $new = (int)$form_state['values'][$list]; // Special handling for the maintainer newsletter. if ($data['maintainer_auto']) { $status = db_result(db_query('SELECT status FROM {cvs_accounts} WHERE uid = %d', $account->uid)); // If CVS account is approved, be sure user is subscribed to the // maintainer newsletter. if ($status == CVS_APPROVED) { if (!$old) { _lists_mailman_subscribe($data, $account); } } // If CVS is not approved, be sure user is not subscribed to the // maintainer newsletter. else { if ($old) { _lists_mailman_unsubscribe($data, $account); } } } elseif ($data['allow_sub'] && ($old != $new)) { if ($new) { _lists_mailman_subscribe($data, $account); } else { _lists_mailman_unsubscribe($data, $account); } } } } /** * Implementation of hook_form_alter(). */ function lists_form_alter(&$form, $form_state, $form_id) { // Add/remove users from maintainer newsletter when their CVS access is // updated. if ($form_id == 'cvs_user_edit_form') { $form['#submit'][] = 'lists_cvs_user_edit_submit'; } else if ($form_id == 'forum_node_form') { $node = $form['#node']; $lists_forum_tids = array_keys(_lists_get_forum_tids()); if (!user_access('post to newsletter')) { // See if user is trying to post to a forum for which they do not have // permission. If so, redirect them back to the forum in question and // notify them they lack permission. if (is_array($node->taxonomy)) { foreach ($node->taxonomy as $tid => $data) { if (in_array($tid, $lists_forum_tids)) { drupal_set_message(t('You do not have permission to post to this forum.')); drupal_goto("forum/$tid"); } } } // See if user has permission to post to mailing lists (generating emails // to potentially large numbers of users). If not, remove these forums // as a selectable forum option. if (is_array($form['taxonomy'])) { foreach ($form['taxonomy'] as $vid => $taxonomy) { if (is_array($taxonomy) && is_array($taxonomy['#options'])) { foreach ($taxonomy['#options'] as $key => $option) { if (is_object($option) && isset($option->option)) { foreach ($option->option as $tid => $name) { if (in_array($tid, $lists_forum_tids)) { unset($form['taxonomy'][$vid]['#options'][$key]->option[$tid]); } } } } } } } } } } /** * Submit newsletter forum to mailman for mass mailing. */ function lists_nodeapi(&$node, $op, $teaser, $page) { global $user; if ($node->status && ($op == 'insert' || $op == 'update') && user_access('post to newsletter')) { // We are going to return if $node->type is not one of the node // types assigned to the forum vocabulary. If forum_nav_vocabulary // is undefined or the vocabulary does not exist, it clearly cannot // be assigned to $node->type, so return to avoid E_ALL warnings. $vid = variable_get('forum_nav_vocabulary', ''); $vocabulary = taxonomy_vocabulary_load($vid); if (empty($vocabulary)) { return; } // Operate only on node types assigned for the forum vocabulary. if (!in_array($node->type, $vocabulary->nodes)) { return; } // Only generate mail for configured forum types. $generate_mail = FALSE; $lists_forum_tids = array_keys(_lists_get_forum_tids()); if (is_array($node->taxonomy)) { foreach ($node->taxonomy as $key => $tid) { if (in_array($tid, $lists_forum_tids)) { $generate_mail = TRUE; $forum_tid = $tid; break; } } } // User has permission to generate emails, so send the node to mailman. if ($generate_mail) { // There's no reason I can see for sending a mailing multiple times, but // since we're tracking whether or not something is sent anyway... $sent = db_result(db_query('SELECT nid FROM {lists_mailman} WHERE nid = %d', $node->nid)); if (!$sent) { $lists = _lists_get_mailman_lists(); $lists_forum_tids = _lists_get_forum_tids(); $list = $lists[$lists_forum_tids[$forum_tid]]; $language = language_default(); $params = array('node' => $node); drupal_mail('lists', 'mailman_newsletter', $list['mail'], $language, $params); db_query('INSERT INTO {lists_mailman} (nid, uid, timestamp) VALUES(%d, %d, %d)', $node->nid, $user->uid, time()); drupal_set_message(t('Mail sent to !email.', array('!email' => $list['mail']))); } // Force comments to be disabled for these posts. David doesn't like this. // This is an ugly kludge. db_query('UPDATE {node} SET comment = %d WHERE nid = %d', COMMENT_NODE_DISABLED, $node->nid); } } } /** * Implementation of hook_mail(). */ function lists_mail($key, &$message, $params) { if ($key == 'mailman_newsletter') { // Just copy over the subject and body as provided. $message['subject'] = $params['node']->title; $message['body'] = drupal_html_to_text($params['node']->body); } } /** * Update maintainer newsletter mailman status when cvs status is updated. */ function lists_cvs_user_edit_submit($form, &$form_state) { if (isset($form_state['values']['cvs_status'])) { $account = user_load(array('uid' => $form_state['values']['cvs_uid'])); $lists = _lists_get_mailman_lists(); $list = $lists['maintainer']; switch ($form_state['values']['cvs_status']) { case CVS_APPROVED: _lists_mailman_subscribe($list, $account); break; case CVS_DISABLED: default: _lists_mailman_unsubscribe($list, $account); break; } } } /** * Helper function to check if the provided email address is susbcribed. * * Some code borrowed from the mailman_api module. */ function _lists_mailman_user_subscribed($list, $account) { $query = array( 'findmember' => $account->mail, 'findmember_btn' => 'Search...' ); $url = url($list['url'] .'/members', array('query' => $query)); $result = lists_mailman_query($url, $list); // replace '@' with '--at--' to match the mailman internal representation so // we don't match our own search query which would always result in a match. return (bool) strpos(strtolower($result->data), str_replace('@', '--at--', $account->mail)); } /** * Helper function to subscribe a user to a mailman mailing list. * * Some code borrowed from the mailmain_api module. */ function _lists_mailman_subscribe($list, $account) { $query = array( 'subscribe_or_invite' => variable_get('lists_mailman_invite', 0), 'send_welcome_msg_to_this_batch' => variable_get('lists_mailman_notify', 0), 'notification_to_list_owner' => variable_get('lists_mailman_notify_admin', 0), 'subscribees_upload' => $account->mail, ); $url = url($list['url'] .'/members/add', array('query' => $query)); $result = lists_mailman_query($url, $list); if ($result !== FALSE) { watchdog('lists', t('User %email subscribed to !list mailman list.', array('%email' => $account->mail, '!list' => $list['name']))); drupal_set_message(t('%email subscribed to %list.', array('%email' => $account->mail, '%list' => $list['name']))); } else { drupal_set_message(t('Failed to subscribe %email to %list. Please try again later.', array('%email' => $account->mail, '%list' => $list['name']))); } } /** * Helper function to subscribe a user to a mailman mailing list. * * Some code borrowed from the mailmain_api module. */ function _lists_mailman_unsubscribe($list, $account) { $query = array( 'send_unsub_ack_to_this_batch' => variable_get('lists_mailman_notify', 0), 'send_unsub_notifications_to_list_owner' => variable_get('lists_mailman_notify_admin', 0), 'unsubscribees_upload' => $account->mail, ); $url = url($list['url'] .'/members/remove', array('query' => $query)); $result = lists_mailman_query($url, $list); if ($result !== FALSE) { watchdog('lists', t('User %email unsubscribed from !list mailman list.', array('%email' => $account->mail, '!list' => $list['name']))); drupal_set_message(t('%email unsubscribed from %list.', array('%email' => $account->mail, '%list' => $list['name']))); } else { drupal_set_message(t('Failed to unsubscribe %email from %list. Please try again later.', array('%email' => $account->mail, '%list' => $list['name']))); } } /** * Connect to admin interface for mailman mailing list and run a request. * * Break out adminpw so we don't log it on errors to minimize the * exposure of this value. * * Some code borrowed from the mailmain_api module. */ function lists_mailman_query($url, $list) { $result = drupal_http_request($url .'&adminpw='. urlencode($list['pass'])); // Provide debug information if connection to mailman fails. if (!is_object($result) || !isset($result->code) || $result->code != '200') { watchdog('lists', t('Mailman HTTP request error (!code) for !list: %error
url=%url', array('!code' => (int)$result->code, '!list' => $list['name'], '%error' => $result->error, '%url' => $url))); return FALSE; } // Provide debug information if connection to mailman succeeds but the list is invalid. elseif (is_object($result) && strpos($result->data, '>No such list')) { watchdog('lists', t('Mailman list !list does not exist.
url=%url', array('!list' => $list['name'], '%url' => $url))); return FALSE; } // All all right, so return full page reply. return $result; }