Mailinglist
 All Data Structures Files Functions Variables Pages
mailinglist_members.module
Go to the documentation of this file.
1 <?php
10 define('MAILINGLIST_SUB_NO', 0);
11 define('MAILINGLIST_SUB_NOMAIL', 1);
12 define('MAILINGLIST_SUB_INDEX', 2);
13 define('MAILINGLIST_SUB_DIGEST', 5);
14 define('MAILINGLIST_SUB_MIME_DIGEST', 6);
15 define('MAILINGLIST_SUB_NORMAL', 10);
16 
17 /******************************************************************************/
18 /* Drupal hooks */
19 /******************************************************************************/
20 
25  $permissions['administer mailinglist members'] = array(
26  'title' => t('Administer Mailinglist Membership'),
27  );
28  $lists = mailinglist_list_load_all(false);
29  foreach ($lists as $index => $list) {
30  $permissions['mailinglist ' . $index . ' member details'] = array(
31  'title' => t($list->admin_title . ' Member Details'),
32  );
33  }
34  return $permissions;
35 }
36 
42  $queue = array(
43  'mailinglist_members' => array(
44  'worker callback' => 'mailinglist_members_callback',
45  'time' => '20',
46  ),
47  );
48  return $queue;
49 }
50 
56  $queue = DrupalQueue::get('mailinglist_members');
57  if($queue->numberOfItems() > 0) {
58  // If still running, don't start more.
59  return;
60  }
61 
62  $lists = mailinglist_list_load_all(false);
63  foreach ($lists as $index => $list) {
64  if (isset($list->operations['members']) && $list->operations['members']) {
65  $queue = DrupalQueue::get('mailinglist_members');
66  $queue->createItem($list->admin_name); // Put item back on list if not done
67  }
68  }
69 }
70 
71 /******************************************************************************/
72 /* Mailinglist hooks */
73 /******************************************************************************/
74 
79  $rolename = 'Mailingling List Subscriber ' . $list->admin_name;
80  if (!user_role_load_by_name($rolename)) {
81  $role = new stdClass();
82  $role->name = $rolename;
83  user_role_save($role);
84  }
85  $list_schema = _mailinglist_members_list_schema($list);
87  foreach ($list_schema as $table_name => $schema) {
88  $ret = db_create_table($table_name, $schema);
89  if ($ret) dpm($ret, $table_name);
90  }
91 }
92 
98  $list_schema = _mailinglist_members_list_schema($list);
100  foreach ($list_schema as $table_name => $schema) {
101  $ret = db_drop_table($table_name);
102  }
103 
104 }
105 
106 /******************************************************************************/
107 
116  $list_name = $list->admin_name;
117  $list_schema = array();
118  $list_schema['mailinglist_members_subscribers__' . $list_name] = array(
119  'description' => 'Table for holding the list of subscribers for the list ' . $list_name,
120  'fields' => array(
121  'sid' => array(
122  'type' => 'serial',
123  'unsigned' => TRUE,
124  'not null' => TRUE,
125  'description' => 'Primary Key for table',
126  ),
127  'email' => array(
128  'type' => 'varchar',
129  'length' => '255',
130  'description' => 'EMail address of subscription'
131  ),
132  'domain' => array(
133  'type' => 'varchar',
134  'length' => '255',
135  'description' => 'Domain of subscription (for stats)'
136  ),
137  'sub_type' => array(
138  'type' => 'int',
139  'description' => 'Type of subscription'
140  ),
141  'last_update' => _mailinglist_datetime_schema( array(
142  'description' => 'Time of last update',
143  'not null' => TRUE,
144  'default' => '0000-00-00 00:00:00',
145  )),
146  'last_update_ind' => _mailinglist_datetime_schema( array(
147  'description' => 'Time of last individual update',
148  'not null' => TRUE,
149  'default' => '0000-00-00 00:00:00',
150  )),
151  'moderated' => array(
152  'type' => 'int',
153  'description' => 'It the subcriber currently moderated',
154  'default' => 0,
155  ),
156  'options' => array(
157  'type' => 'text',
158  'size' => 'big',
159  'serialize' => TRUE,
160  'description' => 'Misc. Options for Subscripton',
161  'object default' => array(
162  ),
163  ),
164  ),
165  'primary key' => array('sid'),
166  'unique key' => array(
167  'email' => array('email'),
168  ),
169  'indexes' => array(
170  'domain' => array('domain'),
171  'last_update' => array('last_update'),
172  'last_update_ind' => array('last_update_ind'),
173  ),
174  );
175  $list_schema['mailinglist_members_subscriptions__' . $list_name] = array(
176  'description' => 'Table for holding info about all subscription/unusubscription events for list',
177  'fields' => array(
178  'sub_key' => array(
179  'type' => 'serial',
180  'unsigned' => TRUE,
181  'not null' => TRUE,
182  'description' => 'Primary Key for table',
183  ),
184  'sid' => array(
185  'type' => 'int',
186  'unsigned' => TRUE,
187  'not null' => TRUE,
188  'description' => 'sid from subscribers table for this subscription',
189  ),
190  'first_seen' => _mailinglist_datetime_schema('Time of first appearance'),
191  'subscribe' => array(
192  'type' => 'int',
193  'default' => 0,
194  'not null' => TRUE,
195  'description' => '1 if the first_seen time is the time of subscription',
196  ),
197  'last_seen' => _mailinglist_datetime_schema('Time of last appearance'),
198  'first_unseen' => _mailinglist_datetime_schema('Time of first dis-appearance'),
199  ),
200  'primary key' => array('sub_key'),
201  'indexes' => array(
202  'first_seen' => array('first_seen'),
203  'last_seen' => array('last_seen'),
204  'first_unseen' => array('first_unseen'),
205  ),
206  'foreign keys' => array(
207  'sid' => array(
208  'table' => 'mailinglist_members_subscribers__' . $list_name,
209  'columns' => array('sid', 'sid'),
210  ),
211  ),
212  );
213  $list->schema($list_schema, 'members');
214  return $list_schema;
215 }
216 
227 function mailinglist_members_memberid($list, $email) {
228  $email = strtolower($email);
229  $list_name = $list->admin_name;
230  $table = 'mailinglist_members_subscribers__' . $list_name;
231  $transaction = db_transaction();
232  $result = db_select($table, 's')
233  -> fields('s')
234  -> condition('s.email', $email, '=')
235  -> execute()
236  -> fetchAssoc();
237  if ($result === FALSE) {
238  $list->log('New Member: ' . $email);
239  $domain = substr($email, strpos($email, '@') +1);
240  $fields = array('email' => $email, 'domain' => $domain);
241  $result['sid'] = db_insert($table) -> fields($fields) -> execute();
242  }
243  return (int)$result['sid'];
244 }
245 
246 function mailinglist_members_update($list, $email, $info) {
247  $email = strtolower($email);
248  $list_name = $list->admin_name;
249  $table = 'mailinglist_members_subscribers__' . $list_name;
250  $transaction = db_transaction();
251  $result = db_select($table, 's')
252  -> fields('s')
253  -> condition('s.email', $email, '=')
254  -> execute()
255  -> fetchAssoc();
256  if ($result == FALSE) {
257  $list->log('Update: Subscriber not present: ' . $email, $list->LOG_ERROR);
258  dpm($email, 'Subscriber Not Present');
259  // Updating someone not present
260  } else {
261  $key = (int)$result['sid'];
262  $newinfo['last_update'] = _mailinglist_datetime_format(time());
263  foreach ($info as $field => $value) {
264  switch($field) {
265  case 'Type':
266  $newinfo['sub_type'] = $value;
267  break;
268  case 'Moderated':
269  $newinfo['moderated'] = $value ? 1 : 0;
270  break;
271  /* Keys that match */
272  case 'last_update_ind':
273  $newinfo[$field] = $value;
274  break;
275  /* All the cases we don't process as they go elsewhere */
276  case 'Email':
277  case 'NoMail': /* Folded into Type */
278  break;
279  case 'Name':
280  default:
281  if (!isset($newinfo['options'])) {
282  if (empty($result['options'])) {
283  $newinfo['options'] = array();
284  } else {
285  $newinfo['options'] = unserialize($result['options']);
286  unset($newinfo['options']['Email']);
287  }
288  }
289  $newinfo['options'][$field] = $value;
290  }
291  }
292 
293  if (isset($newinfo['options'])) {
294  $newinfo['options'] = serialize($newinfo['options']);
295  }
296  $res = db_update($table) -> fields($newinfo) -> condition('sid', $key) -> execute();
297  }
298 }
299 
300 function mailinglist_members_update_ind($list, $email, $info) {
301  $info['last_update_ind'] = _mailinglist_datetime_format(time());
302  mailinglist_members_update($list, $email, $info);
303 }
354 function mailinglist_members_subscribe($list, $email, $time = FALSE) {
355  if ($time === FALSE) $time = time();
356  $time = _mailinglist_datetime_format($time);
357  $list->log('Subscribe @time @email', $list::LOG_DEBUG,
358  array('@email' => $email, '@time' => $time));
359  $sid = mailinglist_members_memberid($list, $email);
360  $list_name = $list->admin_name;
361  $table = 'mailinglist_members_subscriptions__' . $list_name;
363 }
364 
365 function mailinglist_members_unsubscribe($list, $email, $time = FALSE) {
366  if ($time === FALSE) $time = time();
367  $time = _mailinglist_datetime_format($time);
368  $list->log('Unsubscribe @time @email', $list::LOG_DEBUG,
369  array('@email' => $email, '@time' => $time));
370  $sid = mailinglist_members_memberid($list, $email);
371  $list_name = $list->admin_name;
372  $table = 'mailinglist_members_subscriptions__' . $list_name;
374 }
375 
395 function mailinglist_members_seen($list, $email, $time = FALSE) {
396  $level = $list::LOG_DEBUG;
397  if ($time === FALSE) $time = time();
398  $time = _mailinglist_datetime_format($time);
399  $sid = mailinglist_members_memberid($list, $email);
400  $list_name = $list->admin_name;
401  $table = 'mailinglist_members_subscriptions__' . $list_name;
402 
403  $transaction = db_transaction();
404 
405  $sql = "SELECT * FROM {" . $table . "}
406  WHERE sid = :sid and (first_unseen IS NULL or first_unseen = '0000-00-00 00:00:00' or :time <= first_unseen)
407  ORDER BY first_seen ASC LIMIT 1";
408  $result = db_query($sql, array(':sid' => $sid, ':time' => $time)) -> fetchAssoc();
409  if ($result === FALSE) {
410  // No record we can update, either no record at all, or record has an unseen before now
411  $list->log('Newly Seen: ' . $email);
412  $result = array(
413  'sid' => $sid,
414  'first_seen' => $time,
415  'subscribe' => 0,
416  'last_seen' => $time,
417  'first_unseen' => null,
418  );
419  $level = $list::LOG_NOTICE;
420  $result['sub_key'] = db_insert($table) ->fields($result) -> execute();
421  } else {
422  $key = (int) $result['sub_key'];
423  if ($result['first_seen'] > $time) {
424  // We are before the record found.
425  if ($result['subscribe'] > 0) {
426  // We are before the known subsciption date of the record found, make new record.
427  $ts = strtotime($result['first_seen'])-1;
428  $result = array(
429  'sid' => $sid,
430  'first_seen' => $time,
431  'subscribe' => 0,
432  'last_seen' => $time,
433  'first_unseen' => _mailinglist_datetime_format($ts),
434  );
435  $level = $list::NOTICE;
436  $result['sub_key'] = db_insert($table) ->fields($result) -> execute();
437  } else {
438  // We are before record, but it isn't a know subscription, so extend
439 
440  $info = array(
441  'first_seen' => $time,
442  );
443  db_update($table) -> fields($info) -> condition('sub_key', $key) -> execute();
444  }
445  } else if ($result['last_seen'] < $time) {
446  // We are after the end of the block (and before unseen) so extend
447  $info = array(
448  'last_seen' => $time,
449  );
450  $result = db_update($table) -> fields($info) ->
451  condition('sub_key', $key, '=') -> execute();
452  }
453  }
454  $list->log('Seen @time @email', $level,
455  array('@email' => $email, '@time' => $time));
456 }
457 
474 function mailinglist_members_unseen($list, $email, $time = FALSE) {
475  if ($time === FALSE) $time = time();
476  $time = _mailinglist_datetime_format($time);
477  $level = $list::LOG_DEBUG;
478  $sid = mailinglist_members_memberid($list, $email);
479  $list_name = $list->admin_name;
480  $table = 'mailinglist_members_subscriptions__' . $list_name;
481  $transaction = db_transaction();
482  $sql = "SELECT * FROM {" . $table . "}
483  WHERE sid = :sid and first_seen <= :time
484  ORDER BY first_seen DESC LIMIT 1";
485  $result = db_query($sql, array(':sid' => $sid, ':time' => $time)) -> fetchAssoc();
486 
487  if ($result == FALSE) {
488  $list->log('Unseen too Early $time @email', $list::LOG_ERROR, array('@email' => $email, '@time' => $time));
489  } else {
490  $key = (int) $result['sub_key'];
491  if ($time < $result['last_seen']) {
492  $info = array(
493  'sid' => $sid,
494  'first_seen' => $result['last_seen'],
495  'subscribe' => 0,
496  'last_seen' => $result['first_seen'],
497  'first_unseen' => $result['first_unseen'],
498  );
499 
500  db_insert($table) ->fields($info) -> execute();
501 
502  $info = array(
503  'last_seen' => $result['first_seen'],
504  'first_unseen' => $time,
505  );
506 
507  $level = $list::LOG_NOTICE;
508  $result = db_update($table) -> fields($info) ->
509  condition('sub_key', $key, '=') -> execute();
510  } else if ($result['first_unseen'] == null || $time < $result['first_unseen']) {
511  $info = array(
512  'first_unseen' => $time,
513  );
514  $level = $list::LOG_NOTICE;
515  $result = db_update($table) -> fields($info) ->
516  condition('sub_key', $key, '=') -> execute();
517  }
518  }
519  $list->log('Unseen @time @email', $level,
520  array('@email' => $email, '@time' => $time));
521 
523 
524  $table = 'mailinglist_members_subscribers__' . $list_name;
525  $info = array(
526  'sub_type' => MAILINGLIST_SUB_NO,
527  );
528  db_update($table) -> fields($info) -> condition('sid', $sid) -> execute();
529 }
530 
547  $data = $list->getMemberPage($page);
548  foreach ($data['members'] as $email => $info) {
549  // skip a few problematical names that we have hit
550  if(substr($email, 0, 7) == 'mailto:') continue;
551  if(substr($email, -1) == '.') continue;
552  mailinglist_email_name($email, $info['Name']);
553  mailinglist_members_seen($list, $email);
554  mailinglist_members_update($list, $email, $info);
555  }
556  return $data['next'];
557 }
558 
571 function mailinglist_members_scan($list) {
572  // If list hasn't been scanned before, start at beginning.
573  if(!isset($list->parameters['ScanNext'])) {
574  $list->parameters['ScanNext'] = FALSE;
575  }
576 
577  $next = $list->parameters['ScanNext'];
578 
579  if($next === TRUE) {
580  // we have finished scan, check for unprocessed subscribers
581  if(!isset($list->parameters['ScanStart'])) {
582  // Shouldn't happen but disable the scan.
583  $list->parameters['ScanStart'] = '0000';
584  }
585 
586  $sql = 'SELECT * from {mailinglist_members_subscribers__' . $list->admin_name . '}
587  WHERE last_update < :time AND sub_type > 0 LIMIT 1';
588  $result = db_query($sql, array(':time' => $list->parameters['ScanStart']));
589  $flag = 0;
590  while($row = $result -> fetchAssoc()) {
591 dpm($row);
592  $flag = 1;
593  $email = $row['email'];
594  $list->log('Scan Needs Update @time @email', $list::LOG_NOTICE, array('@email' => $email, '@time' => $row['last_update']));
595  _mailinglist_members_lookup($list, $email);
596  }
597  // If we processed no straglers, than prepare to start next round.
598  if (!$flag) {
599  $next = FALSE;
600  }
601  }
602  else {
603  // we are scaning, start at $next (FALSE at begining)
604  if($next === FALSE) {
605  $list->parameters['ScanStart'] = _mailinglist_datetime_format(time());
606  $list->log('Member Scan Start',$list::LOG_NOTICE);
607  }
608 
609  $next = mailinglist_members_process_memberpage($list, $next);
610  if($next === FALSE) {
611  $next = TRUE;
612  }
613 
614  }
615 
617  // Do an update on oldest not individually updated members to fill options array
618  $sql = 'SELECT * from {mailinglist_members_subscribers__' . $list->admin_name . '}
619  WHERE sub_type > 0 ORDER BY last_update_ind ASC, last_update ASC LIMIT 1';
620  $result = db_query($sql, array(':time' => $list->parameters['ScanStart']));
621  while($row = $result -> fetchAssoc()) {
622  $email = $row['email'];
623  $list->log('Individual Update @time @email', $list::LOG_DEBUG,
624  array('@email' => $email, '@time' => $row['last_update_ind']));
625  _mailinglist_members_lookup($list, $email);
626  }
627 
628  $list->parameters['ScanNext'] = $next;
629  $list->log('Next: ' . serialize($next), $list::LOG_DEBUG);
630  ctools_export_crud_save($list->table, $list);
631 
632  return $next !== FALSE;
633 }
634 /******************************************************************************/
635 
636 function mailinglist_members_callback($listname) {
637  if ($listname) {
638  $list = mailinglist_list_load($listname);
639  }
640  else {
641  $list = FALSE;
642  }
643  if($list) {
644 // $list->log('Scanning', $list::LOG_INFO);
645  if(mailinglist_members_scan($list)) {
646  $queue = DrupalQueue::get('mailinglist_members');
647  $queue->createItem($listname); // Put item back on list if not done
648  } else {
649  $list->log('Scan End', $list::LOG_INFO);
650  }
651  }
652 }
653 
654 
655 
660 function _mailinglist_members_lookup($list, $email) {
661  $info = $list->getMemberInfo($email);
662  if ($info) {
663  mailinglist_email_name($email, $info['Name']);
664  mailinglist_members_seen($list, $email);
665  mailinglist_members_update_ind($list, $email, $info);
666  } else {
667  mailinglist_members_unseen($list, $email);
668  }
669 }
670 
676  if ($msg->header('XX-Archive')) {
677 
679  }
680 }
681 
682 
683 /*
684 ******************************************************************************************
685 CTOOLS Hooks
686 ******************************************************************************************
687 */
688 
696 function mailinglist_members_ctools_plugin_directory($owner, $plugin_type) {
697  return 'plugins/' . $owner . '/' . $plugin_type;
698 }
699 
_mailinglist_members_lookup($list, $email)
_mailinglist_members_lookup().
mailinglist_members_unseen($list, $email, $time=FALSE)
Mark a member as haven been not seen (ie wasn't subscribed) at a point in time.
_mailinglist_members_list_schema(MailinglistListInterface $list)
Builds the schema needed by mailinglist_members for a specific list.
mailinglist_members_update($list, $email, $info)
Interface to a mailing list.
mailinglist_members_update_ind($list, $email, $info)
mailinglist_members_cron_queue_info()
Implements hook_cron_queue_info().
const MAILINGLIST_SUB_NO
Defines for sub_types (subscription types)
mailinglist_list_load($list)
Load a list definition.
mailinglist_members_mailinglist_message($msg, $list)
Implements hook_mailinglist_message().
mailinglist_members_process_memberpage($list, $page)
Process one member page from the list.
mailinglist_members_memberid($list, $email)
Get sid for email address.
_mailinglist_datetime_schema($desc)
Return the schema for a 'datetime' field.
mailinglist_members_mailinglist_list_created($list)
Implements hook_mailinglist_list_created().
mailinglist_members_unsubscribe($list, $email, $time=FALSE)
mailinglist_members_cron()
Implements hook_cron().
mailinglist_email_name($email, $name, $count=0)
Lookup Email <-> Name combination.
mailinglist_members_subscribe($list, $email, $time=FALSE)
Subscription tracking routines.
mailinglist_members_ctools_plugin_directory($owner, $plugin_type)
Implements hook_ctools_plugin_directory().
mailinglist_members_mailinglist_list_delete($list)
Implements hook_mailinglist_list_delete().
mailinglist_members_seen($list, $email, $time=FALSE)
Mark a member as having been seen.
mailinglist_members_permission()
Implements hook_permission().
mailinglist_members_scan($list)
Process a partial scan of members for a mailinglist.
mailinglist_list_load_all($show_disabled=TRUE)
Load all mailinglists.
schema(&$schema, $type)
schema();
mailinglist_members_callback($listname)
_mailinglist_datetime_format($ts)
Convert a 'timestamp to a 'datetime'.