'. t('The notification module allows users to subscribe to periodic e-mails which include all new or revised content and/or comments much like the daily news letters sent by some websites. Even if this feature is not configured for normal site users, it can be a useful feature for an administrator of a site to monitor content submissions and comment posts.') .'
'; $output .= ''. t('The administrator sets the frequency of the e-mails in the notify administration interface. They can also set how many e-mail failures should occur before notify stops sending notifications. Note that cron must be enabled for notifications to be sent out.') .'
'; $output .= t('You can
'. t('For more information please read the configuration and customization handbook Notify page.', array('@notify' => 'http://www.drupal.org/handbook/modules/notify/')) .'
'; return $output; } } /** * Menu callback; display notify settings page. */ function notify_admin_settings() { $period = array( 60 => format_interval(60), 300 => format_interval(300), 900 => format_interval(900), 1800 => format_interval(1800), 3600 => format_interval(3600), 10800 => format_interval(10800), 21600 => format_interval(21600), 32400 => format_interval(32400), 43200 => format_interval(43200), 86400 => format_interval(86400), 172800 => format_interval(172800), 259200 => format_interval(259200), 604800 => format_interval(604800), 1209600 => format_interval(1209600), 2419200 => format_interval(2419200), -1 => t('Never'), ); $form = array(); $form['notify_send'] = array( '#type' => 'select', '#title' => t('Send notifications every'), '#default_value' => variable_get('notify_send', 86400), '#options' => $period, '#description' => t('How often should new content notifications be sent? Requires cron to be running.'), ); $form['notify_send_hour'] = array( '#type' => 'select', '#title' => t('Send notifications during this hour'), '#default_value' => variable_get('notify_send_hour', date('G', variable_get('notify_send_last', 0))), '#options' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23), '#description' => t('Hour of the day to send noticiations, on 24 hour clock.'), ); $form['notify_attempts'] = array( '#type' => 'select', '#title' => t('Number of failed sends after which notifications are disabled'), '#default_value' => variable_get('notify_attempts', 5), '#options' => array(t('Disabled'), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20), ); return system_settings_form($form); } /** * Implementation of hook_cron(). */ function notify_cron() { $send_last = variable_get('notify_send_last', 0); $send_interval = variable_get('notify_send', 86400); $send_hour = variable_get('notify_send_hour', date('G', variable_get('notify_send_last', 0))); if ( (time() - $send_last > $send_interval) && (date('G', time()) > $send_hour) && ($send_interval != -1) //special case of settings to send 'never' ) { _notify_send(); variable_set('notify_send_last', time()); } } /** * Implementation of hook_user(). */ function notify_user($type, &$edit, &$user, $category = NULL) { switch ($type) { case 'delete': db_query('DELETE FROM {notify} WHERE uid = %d', $user->uid); break; } } /** * Implementation of hook_perm(). */ function notify_perm() { return array('access notify', 'administer notify','notify unpublished nodes','notify published nodes','notify unpublished comments','notify published comments'); } /** * Implementation of hook_menu(). */ function notify_menu($may_cache) { global $user; $items = array(); if ($may_cache) { $items[] = array('path' => "user/$user->uid/notify", 'title' => t('My notification settings'), 'callback' => 'drupal_get_form', 'callback arguments' => array('notify_user_settings_form', $user->uid), 'access' => user_access('access notify'), 'type' => MENU_LOCAL_TASK, ); $items[] = array('path' => 'admin/user/user/notify', 'title' => t('Notifications'), 'callback' => 'drupal_get_form', 'callback arguments' => 'notify_admin_users', 'access' => user_access('administer notify'), 'type' => MENU_LOCAL_TASK, ); $items[] = array('path' => 'admin/content/notify', 'title' => t('Notification settings'), 'description' => t('Adjust settings for new content notifications sent by e-mail.'), 'callback' => 'drupal_get_form', 'callback arguments' => 'notify_admin_settings', 'access' => user_access('administer notify'), 'type' => MENU_NORMAL_ITEM, ); } else { if (arg(0) == 'user' && is_numeric(arg(1)) && arg(1) != $user->uid) { $account = user_load(array('uid' => arg(1))); if ($account->uid) { $items[] = array( 'path' => 'user/'. $account->uid .'/notify', 'title' => t('Notification settings'), 'callback' => 'drupal_get_form', 'callback arguments' => array('notify_user_settings_form', arg(1)), 'access' => user_access('administer notify'), 'type' => MENU_LOCAL_TASK, ); } } } return $items; } /** * Menu callback; show user notification options. */ function notify_user_settings_form($uid = 0) { global $user; if ($user->uid != $uid && $user->uid != 1) { drupal_access_denied(); return; } $account = user_load(array('uid' => $uid)); if (!is_object($account)) { drupal_not_found(); return; } $result = db_query('SELECT u.uid, u.name, u.mail, n.status, n.node, n.teasers, n.comment FROM {users} u LEFT JOIN {notify} n ON u.uid = n.uid WHERE u.uid = %d AND u.status = 1', $account->uid); $notify = db_fetch_object($result); $form = array(); if (!$notify->mail) { drupal_set_message(t('Your e-mail address must be specified on your my account page.', array('@url' => url('user/'. $account->uid .'/edit'))), 'error'); } $form['notify_page_master'] = array('#type' => 'fieldset', '#title' => t('Master switch')); $form['notify_page_master']['status'] = array('#type' => 'radios', '#title' => t('Notify status'), '#default_value' => $notify->status, '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Do you wish to receive periodic e-mails when new content is posted?'), ); $form['notify_page_detailed'] = array('#type' => 'fieldset', '#title' => t('Detailed settings')); $form['notify_page_detailed']['node'] = array('#type' => 'radios', '#title' => t('Notify new content'), '#default_value' => $notify->node, '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Include new content in the notification mail.'), ); $form['notify_page_detailed']['teasers'] = array('#type' => 'radios', '#title' => t('Content'), '#default_value' => $notify->teasers, '#options' => array(t('Title only'), t('Title + Teaser'), t('Title + Body')), '#description' => t('Select the amount of each post that you would like to see in your notification e-mails.'), ); $form['notify_page_detailed']['comment'] = array('#type' => 'radios', '#title' => t('Notify new comments'), '#default_value' => $notify->comment, '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Include new comments in the notification mail.'), ); $form['uid'] = array('#type' => 'value', '#value' => $account->uid); $form['submit'] = array('#type' => 'submit', '#value' => t('Save settings')); return $form; } function notify_user_settings_form_submit($form_id, $form_values) { db_query('DELETE FROM {notify} WHERE uid = %d', $form_values['uid']); db_query('INSERT INTO {notify} (uid, status, node, teasers, comment) VALUES (%d, %d, %d, %d, %d)', $form_values['uid'], $form_values['status'], $form_values['node'], $form_values['teasers'], $form_values['comment']); drupal_set_message(t('Notify settings saved.')); } /** * Menu callback; show admininster user notification settings form. */ function notify_admin_users() { $form = array(); $form['#tree'] = TRUE; $form['info'] = array('#value' => t('The following table shows all users that have notifications enabled.')); $form['users'] = array(); $result = db_query('SELECT u.uid, u.name, u.mail, n.* FROM {users} u LEFT JOIN {notify} n ON u.uid = n.uid WHERE n.status = 1 AND u.status = 1 ORDER BY u.name'); while ($notify = db_fetch_object($result)) { $form['users'][$notify->uid] = array(); $form['users'][$notify->uid]['name'] = array('#type' => 'markup', '#value' => theme('username', $notify)); $form['users'][$notify->uid]['mail'] = array('#type' => 'markup', '#value' => $notify->mail); $form['users'][$notify->uid]['node'] = array('#type' => 'checkbox', '#default_value' => $notify->node); $form['users'][$notify->uid]['teasers'] = array('#type' => 'select', '#default_value' => $notify->teasers, '#options' => array(t('Title only'), t('Title + Teaser'), t('Title + Body'))); $form['users'][$notify->uid]['comment'] = array('#type' => 'checkbox', '#default_value' => $notify->comment); $form['users'][$notify->uid]['attempts'] = array('#type' => 'markup', '#value' => $notify->attempts ? intval($notify->attempts) : 0); } $form['flush'] = array( '#title' => t('Flush e-mail queue'), '#type' => 'checkbox', '#default_value' => FALSE, '#description' => t('Send out any pending notification e-mails currently in queue.'), ); $form['submit'] = array('#type' => 'submit', '#value' => t('Save settings')); return $form; } /** * Submit for the notify_admin form. */ function notify_admin_users_submit($form_id, $form_values) { if ($form_values['users']) { foreach ($form_values['users'] as $uid => $settings) { db_query('UPDATE {notify} SET node = %d, teasers = %d, comment = %d WHERE uid = %d', $settings['node'], $settings['teasers'], $settings['comment'], $uid); } } drupal_set_message(t('Notify settings saved.')); if ($form_values['flush']) { list($num_sent, $num_failed) = _notify_send(); variable_set('notify_send_last', time()); if ($num_sent > 0) { drupal_set_message(t('!count pending notification e-mails have been sent.', array('!count' => $num_sent))); } elseif ($num_failed > 0) { drupal_set_message(t('!count notification e-mails could not be sent.', array('!count' => $num_failed)), 'error'); } else { drupal_set_message(t('No notification e-mails needed to be sent.')); } } } /** * Theme function to theme the admin user settings form in a table format. */ function theme_notify_admin_users($form) { $output = drupal_render($form['info']); $header = array(t('Username'), t('E-mail address'), t('Content'), t('Teasers'), t('Comment'), t('Failed attempts')); $rows = array(); foreach (element_children($form['users']) as $uid) { $row = array(); foreach (element_children($form['users'][$uid]) as $entry_key) { unset($form['users'][$uid][$entry_key]['#title']); $row[] = drupal_render($form['users'][$uid][$entry_key]); } $rows[] = $row; } if (!$rows) { $rows[] = array(array('data' => t('No users have notifications enabled.'), 'colspan' => 6)); } $output .= theme('table', $header, $rows); $output .= drupal_render($form); return $output; } /** * Formatting of outgoing mail, taken from mail.inc, part of project.module */ function _notify_content($node, $notify) { static $i = 0; switch ($notify->teasers) { case 0: return; case 1: $txt = check_markup($node->teaser, $node->format, FALSE); break; case 2: $txt = check_markup($node->body, $node->format, FALSE); } $pattern = '@((.+?))@ei'; $txt = preg_replace($pattern, "'\\3 ['. _notify_mail_urls('\\2') .']'", $txt); $urls = _notify_mail_urls(); if (count($urls)) { $txt .= "\n"; for ($max = count($urls); $i < $max; $i++) { $txt .= '['. ($i + 1) .'] '. $urls[$i] ."\n"; } } $txt = strip_tags($txt); $txt = notify_entities_to_utf8($txt); return wordwrap($txt, 72); } /** * Format HTML version of outgoing mail */ function _notify_content_html ($node, $notify) { switch ($notify->teasers) { case 0: return; case 1: return check_markup($node->teaser, $node->format, FALSE); break; case 2: return check_markup($node->body, $node->format, FALSE); } } /** * Helper function to send the notification email. * * TODO: Needs some cleanup and themability. */ function _notify_send() { $period = variable_get('notify_send_last', time() - variable_get('notify_send', 86400)); $separator = '------------------------------------------------------------------------------'; $mini_separator = '---'; $num_sent = 0; $num_failed = 0; _notify_switch_user(); // Store current user // Fetch users with notify enabled $uresult = db_query('SELECT u.uid, u.name, u.mail, n.status, n.node, n.teasers, n.comment FROM {notify} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.status = 1 AND u.status = 1 AND n.attempts <= %d', variable_get('notify_attempts', 5)); while ($user = db_fetch_object($uresult)) { // Switch current user to this account to use node_access functions, etc. _notify_switch_user($user->uid); // Fetch all new nodes and 'load' it to get proper body, etc. $nodes = array(); if(user_access('notify published nodes')){ $pubresult = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE (n.status = 1 OR n.moderate = 1) AND n.created > %d AND n.created <= %d ORDER BY n.created'), $period, time()); while ($node = db_fetch_object($pubresult)) { $nodes[$node->nid] = node_load($node->nid); } } if(user_access('notify unpublished nodes')){ $unpubresult = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE n.status <> 1 AND n.moderate <> 1 AND n.created > %d AND n.created <= %d ORDER BY n.created'), $period, time()); while ($node = db_fetch_object($unpubresult)) { $nodes[$node->nid] = node_load($node->nid); } } //$nresult = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE (n.status = 1 OR n.moderate = 1) AND n.created > %d AND n.created <= %d ORDER BY n.created'), $period, time()); // Fetch new comments. $comments = array(); if(user_access('notify published comments')){ $pbresult = db_query(db_rewrite_sql('SELECT c.nid, c.cid, c.subject, c.name FROM {comments} c WHERE c.status = %d AND c.timestamp > %d AND c.timestamp <= %d ORDER BY c.nid, c.timestamp', 'c'), COMMENT_PUBLISHED, $period, time()); while ($comment = db_fetch_object($pbresult)) { $comments[$comment->nid][] = $comment; } } if(user_access('notify unpublished comments')){ $unpresult = db_query(db_rewrite_sql('SELECT c.nid, c.cid, c.subject, c.name FROM {comments} c WHERE c.status <> %d AND c.timestamp > %d AND c.timestamp <= %d ORDER BY c.nid, c.timestamp', 'c'), $period, time()); while ($comment = db_fetch_object($unpresult)) { $comments[$comment->nid][] = $comment; } } $node_body = ''; $comment_body = ''; // Write new node content to e-mail if user has permissions and nodes are // ready to be sent. if ($user->node && user_access('access content') && count($nodes)) { $node_count = 0; foreach ($nodes as $node) { // Skip to next if this user is NOT allowed to view this node. if (!node_access('view', $node)) { continue; } // TODO: Add functionality to hook into moderation modules? if ($node->status == 1) { $status = t('Published'); } elseif ($node->status == 0) { $status = t('Unpublished'); } if ($node_count > 0) { $node_body .= $mini_separator ."\n\n"; } $node_body .= ++$node_count .'. '. $node->title ."\n"; $node_body .= t('!status !type by !author', array('!status' => $status, '!type' => node_get_types('name', $node), '!author' => ($node->name ? $node->name : variable_get('anonymous', 'Anonymous')))) ."\n"; $node_body .= '[ '. url('node/'. $node->nid, NULL, NULL, TRUE) ." ]\n\n"; $node_body .= _notify_content($node, $user) ."\n"; } // Prepend node e-mail header as long as user could access at least one node. if ($node_count > 0) { $node_body = $separator ."\n" . t('Recent content - !count', array('!count' => format_plural(count($nodes), '1 new post', '!count new posts'))) ."\n" . $separator ."\n\n". $node_body; } } // Write new comments to e-mail if user has permissions and there are // comments to be sent. if ($user->comment && user_access('access comments') && count($comments)) { $total_comment_count = 0; foreach ($comments as $nid => $comment) { // If we don't already have the node, fetch it. if (!isset($nodes[$nid])) { $nodes[$nid] = node_load($nid); } // Don't show comments if we're not allowed to view this node. if (!node_access('view', $nodes[$nid], $user->uid)) { continue; } if ($comment_body) { $comment_body .= $mini_separator ."\n\n"; } $comment_body .= t('!count attached to !type posted by !author: !title', array('!count' => format_plural(count($comment), '1 new comment', '!count new comments'), '!title' => $nodes[$nid]->title, '!type' => node_get_types('name', $nodes[$nid]), '!author' => $nodes[$nid]->name ? $nodes[$nid]->name : variable_get('anonymous', 'Anonymous'))) ."\n"; $comment_count = 0; foreach ($comment as $c) { $comment_body .= ' '. ++$comment_count .'. '. t('!title by !author', array('!title' => $c->subject, '!author' => ($c->name ? $c->name : variable_get(anonymous, 'Anonymous')))) ."\n" .' '. url('node/'. $nid, NULL, 'comment-'. $c->cid, TRUE) ."\n\n"; $total_comment_count++; } } if ($total_comment_count > 0) { $comment_body = $separator ."\n" . t('Recent comments - !count', array('!count' => format_plural($total_comment_count, '1 new comment', '!count new comments'))) ."\n" . $separator ."\n\n". $comment_body; } } $body = $node_body . $comment_body; // If there was anything new, send mail. if ($body) { // Set up initial values for e-mail. $from = variable_get('site_mail', ini_get('sendmail_from')); $from_name = variable_get('site_name', 'Drupal'); $subject = t('!sitename new content notification for !username', array('!username' => $user->name, '!sitename' => variable_get('site_name', 'Drupal'))); $body = t('Greetings !user,', array('!user' => $user->name)) ."\n\n". $body; $body .= "\n-- \n"; $body .= t('This is an automatic e-mail from !sitename.', array('!sitename' => variable_get('site_name', 'Drupal')))."\n"; $body .= t('To stop receiving these e-mails, change your notification preferences at !notify-url', array('!notify-url' => url("user/$user->uid/notify" , NULL, NULL, TRUE)))."\n"; $headers = array();//'From' => "$from_name <$from>"); if (!drupal_mail('notify_mail', $user->mail, $subject, wordwrap($body, 72), $from, $headers)) { $num_failed++; db_query('UPDATE {notify} SET attempts = attempts + 1 WHERE uid = %d', $user->uid); watchdog('error', t('Notify: User %name (%mail) could not be notified. Mail error.', array('%name' => $user->name, '%mail' => $user->mail))); } else { $num_sent++; watchdog('user', t('Notify: User %name (%mail) notified successfully.', array('%name' => $user->name, '%mail' => $user->mail))); } } } // Restore user. _notify_switch_user(); return array($num_sent, $num_failed); } function notify_entities_to_utf8($text) { static $table; // We store named entities in a table for quick processing. if (!isset($table)) { // Get all named HTML entities. $table = array_flip(get_html_translation_table(HTML_ENTITIES)); // PHP gives us Windows-1252/ISO-8859-1 data, we need UTF-8. $table = array_map('utf8_encode', $table); } $text = strtr($text, $table); // Any remaining entities are numerical. Use a regexp to replace them. return preg_replace('/(x?)([A-Za-z0-9]+);/e', '_notify_entity_to_utf8("$1", "$2")', $text); } function _notify_entity_to_utf8($hex, $codepoint) { if ($hex != '') { $codepoint = base_convert($codepoint, 16, 10); } if ($codepoint < 0x80) { return chr($codepoint); } else if ($codepoint < 0x800) { return chr(0xC0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3F)); } else if ($codepoint < 0x10000) { return chr(0xE0 | ( $codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3F)) . chr(0x80 | ( $codepoint & 0x3F)); } else if ($codepoint < 0x200000) { return chr(0xF0 | ( $codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3F)) . chr(0x80 | (($codepoint >> 6) & 0x3F)) . chr(0x80 | ( $codepoint & 0x3F)); } } function _notify_mail_urls($url = 0) { static $urls = array(); if ($url) { if (strpos($url, '://')) { $urls[] = $url; } elseif (strpos($url, '/')==0) { $urls[] = url(substr($url,1) , NULL, NULL, TRUE); } else { $urls[] = url($url, NULL, NULL, TRUE); } return count($urls); } return $urls; } /** * Switch from original user to mail submision user and back. * * NOTE: Copied from mailhandler * * Note: You first need to run _notify_switch_user without * argument to store the current user. Call _notify_switch_user * without argument to set the user back to the original user. * * @param $uid The user ID to switch to */ function _notify_switch_user($uid = NULL) { global $user; static $orig_user = array(); if (isset($uid)) { // Should a user visit cron.php, or should this module be invoked via // poormanscron and _notify_send() does not complete, // the visitor will end up logged in as the "switched to user" for // subsequent requests unless we disable saving the session // until we are sure we're the invoking user again. session_save_session(FALSE); $user = user_load(array('uid' => $uid)); } // Retrieve the initial user, can be called multiple times. else if (count($orig_user)) { $user = array_shift($orig_user); array_unshift($orig_user, $user); session_save_session(TRUE); } // Store the initial user. else { $orig_user[] = $user; } }