/[drupal]/contributions/modules/drupalorg/lists/lists.module
ViewVC logotype

Contents of /contributions/modules/drupalorg/lists/lists.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.9 - (show annotations) (download) (as text)
Sat Feb 21 14:12:38 2009 UTC (9 months ago) by goba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +4 -4 lines
File MIME type: text/x-php
#377874 reported by webchick, Heine; debugged by dww, webernet: lists.module disabled commenting on all nodes when an admin was editing them; should only disable on newsletter nodes
1 <?php
2 // $Id: lists.module,v 1.8 2009/02/20 13:11:38 goba Exp $
3
4 /**
5 * @file
6 * Drupal.org mailing list subscription helper.
7 *
8 * How to configure mailman mailing lists with this module (there is no admin
9 * interface for this at this time):
10 *
11 * 1) Configure mailman lists as necessary. It should be configured so that
12 * only messages from the drupal website mailing address are allowed to post
13 * to the mailing list. (Posting to specific forums will generate an email
14 * sent from the drupal.org website to mailman, which will then be sent to
15 * everyone subscribed to the mailman list.)
16 *
17 * 2) Update function _lists_get_mailman_lists() with list configuration. The
18 * individual list password should be stored directly in the database as a
19 * variable to avoid distributing it to everyone via source code or
20 * settings.php).
21 *
22 * The following lists are already added: 'Drupal newsletter', 'Security
23 * announcements', and 'Maintainer news'. You need to configure 'mail',
24 * 'pass', and 'url' for each. 'mail' is the email address that mail needs to
25 * be sent to when posting to the mailing list. 'pass' is the admin password
26 * for this mailing list (this is sent to the server in the URL itself!).
27 * 'url' is the HTTP url to the administrative interface for the given mailing
28 * list.
29 *
30 * The 'Maintainer news' is a special newsletter that users can not manually
31 * subscribe/unsubscribe. Instead, they will be automatically
32 * subscribed/unsubscribed based on whether or not they have CVS commit access.
33 *
34 * 3) Create a Forum container named "Newsletters", then create forums for each
35 * mailman-powered newsletter within this new container. For the current lists,
36 * this means adding the following lists:
37 * • Security newsletter
38 * • Drupal newsletter
39 * • Maintainer newsletter
40 *
41 * 4) Update function _lists_get_forum_tids(), adding a correlation between the
42 * tid of each forum and the list's key in _lists_get_mailman_lists().
43 *
44 * 5) If this doesn't seem to work, file an issue and then email
45 * jeremy@tag1consulting.com to tell him to review it.
46 */
47
48 /**
49 * Implementation of hook_menu().
50 */
51 function lists_menu() {
52 $items['lists'] = array(
53 'title' => 'Mailing lists',
54 'access arguments' => array('access content'),
55 'page callback' => 'drupal_get_form',
56 'page arguments' => array('lists_subscribe_form'),
57 'type' => MENU_SUGGESTED_ITEM
58 );
59 return $items;
60 }
61
62 // == Mailing list functionality ===============================================
63
64 /**
65 * Internal helper function to return a list of mailing lists.
66 */
67 function _lists_get_lists() {
68 $lists = array(
69 'support' => array(
70 'name' => 'Support',
71 'description' => 'A list for support questions.',
72 'mailto' => 'support-request@drupal.org',
73 ),
74 'development' => array(
75 'name' => 'Development',
76 'description' => 'A list for Drupal developers.',
77 'mailto' => 'development-request@drupal.org',
78 ),
79 'themes' => array(
80 'name' => 'Themes',
81 'description' => 'A list for Drupal theme developers/designers.',
82 'mailto' => 'themes-request@drupal.org',
83 ),
84 'documentation' => array(
85 'name' => 'Documentation',
86 'description' => 'A list for documentation contributors.',
87 'mailto' => 'documentation-request@drupal.org',
88 ),
89 'translations' => array(
90 'name' => 'Translations',
91 'description' => 'A list for Drupal UI translators.',
92 'mailto' => 'translations-request@drupal.org',
93 ),
94 'consulting' => array(
95 'name' => 'Consulting',
96 'description' => 'A mailing list for Drupal consultants and Drupal service/hosting providers.',
97 'mailto' => 'consulting-request@drupal.org',
98 ),
99 'drupal-cvs' => array(
100 'name' => 'CVS commits',
101 'description' => 'A list with all CVS commit messages.',
102 'mailto' => 'drupal-cvs-request@drupal.org',
103 ),
104 'cvs-applications' => array(
105 'name' => 'CVS applications',
106 'description' => 'A list of all applications for an account in the Drupal contributions CVS repository.',
107 'mailto' => 'cvs-applications-request@drupal.org',
108 'private' => TRUE,
109 ),
110 'webmasters' => array(
111 'name' => 'Webmasters',
112 'description' => 'A list for drupal.org webmasters (e.g. settings and content on the drupal.org website, user management, removing spam, etc.).',
113 'mailto' => 'webmasters-request@drupal.org',
114 'private' => TRUE,
115 ),
116 'infrastructure' => array(
117 'name' => 'Infrastructure',
118 'description' => 'A list for drupal.org infrastructure maintainers (e.g. drupal.org hardware and server configuration, the CVS repository, mailing lists, etc).',
119 'mailto' => 'infrastructure-request@drupal.org',
120 'private' => TRUE,
121 ),
122 'drupal-con' => array(
123 'name' => 'DrupalCON',
124 'description' => 'A list for the organization of Drupal conferences and events.',
125 'mailto' => 'drupal-con-request@drupal.org',
126 'private' => TRUE,
127 ),
128 );
129 return $lists;
130 }
131
132 /**
133 * Mailing list form builder function.
134 *
135 * @see lists_subscribe_form_validate()
136 * @see lists_subscribe_form_submit()
137 */
138 function lists_subscribe_form() {
139 global $user;
140 $lists = _lists_get_lists();
141
142 foreach ($lists as $list => $info) {
143 $links = array();
144 if (isset($info['private']) && $info['private']) {
145 $links[] = "<a href=\"http://lists.drupal.org/private/$list/\">view archive</a> (members only)";
146 }
147 else {
148 $links[] = "<a href=\"http://lists.drupal.org/pipermail/$list/\">view archive</a>";
149 $links[] = "<a href=\"http://lists.drupal.org/archives/cgi-bin/namazu.cgi?idxname=$list\">search archive</a>";
150 }
151
152 $output .= '<h2>'. $info['name'] ."</h2>\n";
153 if (!$info['disabled']) {
154 $links[] = "<a href=\"http://lists.drupal.org/listinfo/$list\">mailman page</a>";
155 }
156 else {
157 $output .= "<em>This list has been disabled.</em>\n";
158 }
159
160 $output .= '<p>'. $info['description'] ."</p>\n";
161 $output .= '<p>'. implode(' . ', $links) ."</p>\n";
162 }
163 $output .= '<h2>Subscribe</h2>';
164
165 $form['intro'] = array(
166 '#type' => 'markup',
167 '#value' => $output,
168 );
169
170 $form['mail'] = array(
171 '#type' => 'textfield',
172 '#title' => t('E-mail address'),
173 '#default_value' => ($user->uid ? $user->mail : ''),
174 '#size' => 50,
175 '#maxlength' => 255,
176 '#required' => TRUE,
177 );
178 $form['item'] = array(
179 '#type' => 'item',
180 '#title' => t('Mailing lists'),
181 '#default_value' => '',
182 );
183 foreach ($lists as $list => $info) {
184 if (!$info['disabled']) {
185 $form[$list] = array(
186 '#type' => 'checkbox',
187 '#title' => $list,
188 );
189 }
190 }
191 $form['subscribe'] = array(
192 '#type' => 'submit',
193 '#value' => t('Subscribe'),
194 );
195
196 return $form;
197 }
198
199 /**
200 * Validate form submission.
201 *
202 * @see lists_subscribe_form()
203 */
204 function lists_subscribe_form_validate($form, &$form_state) {
205 if (!valid_email_address($form_state['values']['mail'])) {
206 form_set_error('mail', t('Please enter a valid e-mail address.'));
207 }
208 }
209
210 /**
211 * Submission handler for the subscription form.
212 *
213 * @see lists_subscribe_form()
214 */
215 function lists_subscribe_form_submit($form, &$form_state) {
216 $sent = FALSE;
217 $mail = $form_state['values']['mail'];
218 if ($mail) {
219 foreach (_lists_get_lists() as $list => $info) {
220 if ($form_state['values'][$list]) {
221 $sent = TRUE;
222 $headers = "From: $mail\nReturn-path: $mail\nError-to: $mail";
223 $result[] = mail($info['mailto'], "subscribe address=$mail", 'subscribe to mailing list', $headers);
224 }
225 }
226 }
227
228 if (!$sent) {
229 drupal_set_message(t('You did not fill in the form properly.'), 'error');
230 }
231 else {
232 drupal_set_message(t('You will receive confirmation emails for your subscriptions. Please read them carefully and follow the directions.'));
233 }
234 }
235
236 // == Newsletters ==============================================================
237
238 /**
239 * Static definition of all mailman managed Drupal mailing lists.
240 */
241 function _lists_get_mailman_lists() {
242 // passwords need to be set in variable table in order to be able to
243 // administer these lists.
244 $lists = array(
245 'drupal' => array(
246 'name' => 'Drupal newsletter',
247 'description' => 'A sometimes-monthly mailing about all things Drupal.',
248 'allow_sub' => TRUE,
249 'mail' => variable_get('lists_drupal_mail', ''),
250 'pass' => variable_get('lists_drupal_pass', ''),
251 'url' => variable_get('lists_drupal_url', ''),
252 ),
253 'security' => array(
254 'name' => 'Security announcements',
255 'description' => 'A low volume mailing list where all security issues affecting Drupal and Drupal contributed modules are publically announced.',
256 'allow_sub' => TRUE,
257 'mail' => variable_get('lists_security_mail', ''),
258 'pass' => variable_get('lists_security_pass', ''),
259 'url' => variable_get('lists_security_url', ''),
260 ),
261 'maintainer' => array(
262 'name' => 'Maintainer news',
263 'description' => 'This list is automatically enabled for users with a CVS account and can not be manually subscribed or unsubscribed.',
264 'allow_sub' => FALSE,
265 'maintainer_auto' => TRUE,
266 'mail' => variable_get('lists_maintainer_mail', ''),
267 'pass' => variable_get('lists_maintainer_pass', ''),
268 'url' => variable_get('lists_maintainer_url', ''),
269 ),
270 );
271 return $lists;
272 }
273
274 /**
275 * List of all forums that are mailman mailing lists.
276 */
277 function _lists_get_forum_tids() {
278 return variable_get('lists_forum_tids', array());
279 }
280
281 /**
282 * Implementation of hook_perm().
283 */
284 function lists_perm() {
285 return array('post to newsletter');
286 }
287
288 /**
289 * Implementation of hook_user().
290 *
291 * We need to hack this in, since user module does try to have a tight control
292 * of management of these menu items, so we need to get it created through
293 * this hook and then altered by hook_menu_alter(). Drupal 7 should do better.
294 */
295 function lists_user($op, &$edit, &$account, $category = NULL) {
296 switch ($op) {
297 case 'categories':
298 return array(array('name' => 'newsletter', 'title' => t('My newsletters'), 'weight' => 10));
299 }
300 }
301
302 /**
303 * Implementation of hook_menu_alter().
304 *
305 * See why we need this hack in lists_user().
306 */
307 function lists_menu_alter(&$callbacks) {
308 $callbacks['user/%user_category/edit/newsletter']['page callback'] = 'drupal_get_form';
309 $callbacks['user/%user_category/edit/newsletter']['page arguments'] = array('lists_mailman_user_form', 1);
310 }
311
312 /**
313 * Per-use mailman subscription form.
314 *
315 * @ingroup forms
316 * @see lists_mailman_user_form_submit()
317 */
318 function lists_mailman_user_form(&$form_state, $account = FALSE) {
319 $form = array();
320
321 $form['newsletter'] = array(
322 '#type' => 'fieldset',
323 '#title' => t('Newsletter subscriptions'),
324 '#description' => t('Select the newsletter(s) which you want to subcribe or unsubcribe.'),
325 '#collapsible' => FALSE,
326 );
327
328 $lists = _lists_get_mailman_lists();
329 foreach ($lists as $list => $info) {
330 $default_value = _lists_mailman_user_subscribed($info, $account);
331 $form['newsletter'][$list] = array(
332 '#type' => 'checkbox',
333 '#title' => $info['name'],
334 '#description' => $info['description'],
335 '#disabled' => !$info['allow_sub'],
336 '#default_value' => $default_value,
337 );
338 }
339 $form['uid'] = array(
340 '#type' => 'hidden',
341 '#value' => $account->uid,
342 );
343 $form['submit'] = array(
344 '#type' => 'submit',
345 '#value' => t('Save'),
346 );
347 return $form;
348 }
349
350 /**
351 * Subscribe/unsubscribe from mailman mailing lists.
352 */
353 function lists_mailman_user_form_submit($form, &$form_state) {
354 $account = user_load(array('uid' => $form_state['values']['uid']));
355 $lists = _lists_get_mailman_lists();
356 foreach ($lists as $list => $data) {
357 $old = (int)$form['newsletter'][$list]['#default_value'];
358 $new = (int)$form_state['values'][$list];
359 if ($data['allow_sub'] && ($old != $new)) {
360 if ($new) {
361 _lists_mailman_subscribe($data, $account);
362 }
363 else {
364 _lists_mailman_unsubscribe($data, $account);
365 }
366 }
367 // Special handling for the maintainer newsletter.
368 else if ($data['maintainer_auto']) {
369 $status = db_query('SELECT status FROM {cvs_accounts} WHERE uid = %d', $account->uid);
370 // If CVS account is approved, be sure user is subscribed to the
371 // maintainer newsletter.
372 if ($status == CVS_APPROVED) {
373 if (!$old) {
374 _lists_mailman_subscribe($data, $account);
375 }
376 }
377 // If CVS is not approved, be sure user is not subscribed to the
378 // maintainer newsletter.
379 else {
380 if ($old) {
381 _lists_mailman_unsubscribe($data, $account);
382 }
383 }
384 }
385 }
386 }
387
388 /**
389 * Implementation of hook_form_alter().
390 */
391 function lists_form_alter(&$form, $form_state, $form_id) {
392 // Add/remove users from maintainer newsletter when their CVS access is
393 // updated.
394 if ($form_id == 'cvs_user_edit_form') {
395 $form['#submit'][] = 'lists_cvs_user_edit_submit';
396 }
397 else if ($form_id == 'forum_node_form') {
398 $node = $form['#node'];
399 $lists_forum_tids = array_keys(_lists_get_forum_tids());
400 if (!user_access('post to newsletter')) {
401 // See if user is trying to post to a forum for which they do not have
402 // permission. If so, redirect them back to the forum in question and
403 // notify them they lack permission.
404 if (is_array($node->taxonomy)) {
405 foreach ($node->taxonomy as $tid => $data) {
406 if (in_array($tid, $lists_forum_tids)) {
407 drupal_set_message(t('You do not have permission to post to this forum.'));
408 drupal_goto("forum/$tid");
409 }
410 }
411 }
412 // See if user has permission to post to mailing lists (generating emails
413 // to potentially large numbers of users). If not, remove these forums
414 // as a selectable forum option.
415 if (is_array($form['taxonomy'])) {
416 foreach ($form['taxonomy'] as $vid => $taxonomy) {
417 if (is_array($taxonomy) && is_array($taxonomy['#options'])) {
418 foreach ($taxonomy['#options'] as $key => $option) {
419 if (is_object($option) && isset($option->option)) {
420 foreach ($option->option as $tid => $name) {
421 if (in_array($tid, $lists_forum_tids)) {
422 unset($form['taxonomy'][$vid]['#options'][$key]->option[$tid]);
423 }
424 }
425 }
426 }
427 }
428 }
429 }
430 }
431 }
432 }
433
434 /**
435 * Submit newsletter forum to mailman for mass mailing.
436 */
437 function lists_nodeapi(&$node, $op, $teaser, $page) {
438 global $user;
439
440 if ($node->status && ($op == 'insert' || $op == 'update') && user_access('post to newsletter')) {
441 // We are going to return if $node->type is not one of the node
442 // types assigned to the forum vocabulary. If forum_nav_vocabulary
443 // is undefined or the vocabulary does not exist, it clearly cannot
444 // be assigned to $node->type, so return to avoid E_ALL warnings.
445 $vid = variable_get('forum_nav_vocabulary', '');
446 $vocabulary = taxonomy_vocabulary_load($vid);
447 if (empty($vocabulary)) {
448 return;
449 }
450
451 // Operate only on node types assigned for the forum vocabulary.
452 if (!in_array($node->type, $vocabulary->nodes)) {
453 return;
454 }
455
456 // Only generate mail for configured forum types.
457 $generate_mail = FALSE;
458 $lists_forum_tids = array_keys(_lists_get_forum_tids());
459 if (is_array($node->taxonomy)) {
460 foreach ($node->taxonomy as $key => $tid) {
461 if (in_array($tid, $lists_forum_tids)) {
462 $generate_mail = TRUE;
463 $forum_tid = $tid;
464 break;
465 }
466 }
467 }
468
469 // User has permission to generate emails, so send the node to mailman.
470 if ($generate_mail) {
471 // There's no reason I can see for sending a mailing multiple times, but
472 // since we're tracking whether or not something is sent anyway...
473 $sent = db_result(db_query('SELECT nid FROM {lists_mailman} WHERE nid = %d', $node->nid));
474 if (!$sent) {
475 $lists = _lists_get_mailman_lists();
476 $lists_forum_tids = _lists_get_forum_tids();
477 $list = $lists[$lists_forum_tids[$forum_tid]];
478 $headers = array(
479 'MIME-Version' => '1.0',
480 'Content-Type' => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
481 'Content-Transfer-Encoding' => '8Bit',
482 'X-Mailer' => 'Drupal'
483 );
484 drupal_mail_send(array('id' => 'lists_mailman_newsletter', 'to' => $list['mail'], 'subject' => $node->title, 'body' => drupal_html_to_text($node->body), 'headers' => $headers));
485 db_query('INSERT INTO {lists_mailman} (nid, uid, timestamp) VALUES(%d, %d, %d)', $node->nid, $user->uid, time());
486 drupal_set_message(t('Mail sent to !email.', array('!email' => $list['mail'])));
487 }
488 // Force comments to be disabled for these posts. David doesn't like this.
489 // This is an ugly kludge.
490 db_query('UPDATE {node} SET comment = %d WHERE nid = %d', COMMENT_NODE_DISABLED, $node->nid);
491 }
492 }
493 }
494
495 /**
496 * Update maintainer newsletter mailman status when cvs status is updated.
497 */
498 function lists_cvs_user_edit_submit($form, &$form_state) {
499 if (isset($form_state['values']['cvs_status'])) {
500 $account = user_load(array('uid' => $form_state['values']['cvs_uid']));
501 $lists = _lists_get_mailman_lists();
502 $list = $lists['maintainer'];
503 switch ($form_state['values']['cvs_status']) {
504 case CVS_APPROVED:
505 _lists_mailman_subscribe($list, $account);
506 break;
507 case CVS_DISABLED:
508 default:
509 _lists_mailman_unsubscribe($list, $account);
510 break;
511 }
512 }
513 }
514
515 /**
516 * Helper function to check if the provided email address is susbcribed.
517 *
518 * Some code borrowed from the mailman_api module.
519 */
520 function _lists_mailman_user_subscribed($list, $account) {
521 $query = array(
522 'findmember' => $account->mail,
523 'findmember_btn' => 'Search...'
524 );
525 $url = url($list['url'] .'/members', array('query' => $query));
526 $result = lists_mailman_query($url, $list);
527 // replace '@' with '--at--' to match the mailman internal representation so
528 // we don't match our own search query which would always result in a match.
529 return (bool) strpos(strtolower($result->data), str_replace('@', '--at--', $account->mail));
530 }
531
532 /**
533 * Helper function to subscribe a user to a mailman mailing list.
534 *
535 * Some code borrowed from the mailmain_api module.
536 */
537 function _lists_mailman_subscribe($list, $account) {
538 $query = array(
539 'subscribe_or_invite' => variable_get('lists_mailman_invite', 0),
540 'send_welcome_msg_to_this_batch' => variable_get('lists_mailman_notify', 0),
541 'notification_to_list_owner' => variable_get('lists_mailman_notify_admin', 0),
542 'subscribees_upload' => $account->mail,
543 );
544 $url = url($list['url'] .'/members/add', array('query' => $query));
545 $result = lists_mailman_query($url, $list);
546 if ($result !== FALSE) {
547 watchdog('lists', t('User %email subscribed to !list mailman list.', array('%email' => $account->mail, '!list' => $list['name'])));
548 drupal_set_message(t('%email subscribed to %list.', array('%email' => $account->mail, '%list' => $list['name'])));
549 }
550 else {
551 drupal_set_message(t('Failed to subscribe %email to %list. Please try again later.', array('%email' => $account->mail, '%list' => $list['name'])));
552 }
553 }
554
555 /**
556 * Helper function to subscribe a user to a mailman mailing list.
557 *
558 * Some code borrowed from the mailmain_api module.
559 */
560 function _lists_mailman_unsubscribe($list, $account) {
561 $query = array(
562 'send_unsub_ack_to_this_batch' => variable_get('lists_mailman_notify', 0),
563 'send_unsub_notifications_to_list_owner' => variable_get('lists_mailman_notify_admin', 0),
564 'unsubscribees_upload' => $account->mail,
565 );
566 $url = url($list['url'] .'/members/remove', array('query' => $query));
567 $result = lists_mailman_query($url, $list);
568 if ($result !== FALSE) {
569 watchdog('lists', t('User %email unsubscribed from !list mailman list.', array('%email' => $account->mail, '!list' => $list['name'])));
570 drupal_set_message(t('%email unsubscribed from %list.', array('%email' => $account->mail, '%list' => $list['name'])));
571 }
572 else {
573 drupal_set_message(t('Failed to unsubscribe %email from %list. Please try again later.', array('%email' => $account->mail, '%list' => $list['name'])));
574 }
575 }
576
577 /**
578 * Connect to admin interface for mailman mailing list and run a request.
579 *
580 * Break out adminpw so we don't log it on errors to minimize the
581 * exposure of this value.
582 *
583 * Some code borrowed from the mailmain_api module.
584 */
585 function lists_mailman_query($url, $list) {
586 $result = drupal_http_request($url .'&adminpw='. urlencode($list['pass']));
587
588 // Provide debug information if connection to mailman fails.
589 if (!is_object($result) || !isset($result->code) || $result->code != '200') {
590 watchdog('lists', t('Mailman HTTP request error (!code) for !list: %error<br />url=%url', array('!code' => (int)$result->code, '!list' => $list['name'], '%error' => $result->error, '%url' => $url)));
591 return FALSE;
592 }
593
594 // Provide debug information if connection to mailman succeeds but the list is invalid.
595 elseif (is_object($result) && strpos($result->data, '>No such list')) {
596 watchdog('lists', t('Mailman list !list does not exist.<br />url=%url', array('!list' => $list['name'], '%url' => $url)));
597 return FALSE;
598 }
599
600 // All all right, so return full page reply.
601 return $result;
602 }

  ViewVC Help
Powered by ViewVC 1.1.2