'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;
}