| 1 |
<?php
|
| 2 |
// $Id: subscriptions_mail.cron.inc,v 1.2 2009/10/04 15:51:07 salvis Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Subscriptions module mail gateway (cron functions).
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* Implementation of hook_cron().
|
| 11 |
*
|
| 12 |
* Takes items from {subscriptions_queue} and generates notification emails.
|
| 13 |
*/
|
| 14 |
function _subscriptions_mail_cron() {
|
| 15 |
|
| 16 |
// set_time_limit(3600); drupal_set_message('DON\'T FORGET TO REMOVE THE TIME LIMIT EXTENSION!!!');
|
| 17 |
|
| 18 |
global $user, $language;
|
| 19 |
drupal_init_language();
|
| 20 |
include_once drupal_get_path('module', 'subscriptions_mail') .'/subscriptions_mail.templates.inc';
|
| 21 |
|
| 22 |
$mails_allowed = variable_get('subscriptions_number_of_mails', 0);
|
| 23 |
$from = _subscriptions_mail_site_mail();
|
| 24 |
$old_uid = 0;
|
| 25 |
$single_count = 0;
|
| 26 |
$digest_count = 0;
|
| 27 |
$loaded_objects = array();
|
| 28 |
$users = array();
|
| 29 |
$fields = array();
|
| 30 |
|
| 31 |
// Strategy for cron:
|
| 32 |
// Use a defined percentage of the total cron time (default: 50%), but leave at least 5s.
|
| 33 |
$total_seconds = ini_get('max_execution_time');
|
| 34 |
if (empty($total_seconds)) {
|
| 35 |
// Work-around for D6.14, which broke this in #193383-106 with http://cvs.drupal.org/viewvc.py/drupal/drupal/includes/common.inc?r1=1.756.2.65&r2=1.756.2.66&pathrev=DRUPAL-6
|
| 36 |
// D6.15 will revert this to a hard-coded 240 in http://cvs.drupal.org/viewvc.py/drupal/drupal/includes/common.inc?r1=1.756.2.70&r2=1.756.2.71&pathrev=DRUPAL-6
|
| 37 |
$total_seconds = 240;
|
| 38 |
}
|
| 39 |
$lost_seconds = timer_read('page')/1000;
|
| 40 |
$available_seconds = $total_seconds - $lost_seconds;
|
| 41 |
$usable_seconds = min(array($available_seconds - 5, $total_seconds*subscriptions_mail_get_cron_percentage()/100));
|
| 42 |
//TEST: watchdog('cron', "Subscriptions has $available_seconds of $total_seconds seconds available.");
|
| 43 |
if ($usable_seconds <= 0) {
|
| 44 |
$variables = array(
|
| 45 |
'!Module' => 'Subscriptions',
|
| 46 |
'@link' => url('admin/settings/subscriptions', array('fragment' => 'edit-subscriptions-cron-percent')),
|
| 47 |
);
|
| 48 |
$watchdog = 'watchdog'; // keep potx from translating 'cron'
|
| 49 |
if ($usable_seconds == 0) {
|
| 50 |
$watchdog('cron', t('!Module cannot send any notifications because its <a href="@link">cron job time</a> is 0!', $variables), NULL, WATCHDOG_WARNING);
|
| 51 |
}
|
| 52 |
else {
|
| 53 |
$watchdog('cron', t('!Module cannot send any notifications because the cron run has less than 5 available seconds left!', $variables), NULL, WATCHDOG_WARNING);
|
| 54 |
}
|
| 55 |
}
|
| 56 |
|
| 57 |
while ((empty($mails_allowed) || $single_count + $digest_count < $mails_allowed)
|
| 58 |
&& timer_read('page')/1000 < $lost_seconds + $usable_seconds) {
|
| 59 |
$result = db_query_range('SELECT * FROM {subscriptions_queue} WHERE last_sent + send_interval < %d ORDER BY sqid', time(), 0, 1);
|
| 60 |
if (!($s = db_fetch_array($result))) {
|
| 61 |
break; // No more ready queue items, terminate loop.
|
| 62 |
}
|
| 63 |
|
| 64 |
if (!isset($users[$s['uid']])) {
|
| 65 |
$users[$s['uid']] = user_load(array('uid' => $s['uid']));
|
| 66 |
}
|
| 67 |
$saved_user = $user;
|
| 68 |
$saved_language = $language;
|
| 69 |
session_save_session(FALSE);
|
| 70 |
$user = $users[$s['uid']];
|
| 71 |
drupal_init_language();
|
| 72 |
$langcode = $language->language;
|
| 73 |
$user_mails = array();
|
| 74 |
|
| 75 |
do { // once and repeat while adding to a digest
|
| 76 |
if ($user->status && $user->access) {
|
| 77 |
$cids = array();
|
| 78 |
$load_function = $s['load_function'];
|
| 79 |
$index = $load_args = $s['load_args'];
|
| 80 |
if (!isset($loaded_objects[$user->uid][$load_function][$load_args])) {
|
| 81 |
if (is_numeric($load_args)) {
|
| 82 |
$object = $load_function($load_args, $s['sqid'], $s['is_new']);
|
| 83 |
}
|
| 84 |
else {
|
| 85 |
$load_args = unserialize($load_args);
|
| 86 |
$load_args[] = $s['is_new'];
|
| 87 |
$object = call_user_func_array($load_function, $load_args);
|
| 88 |
}
|
| 89 |
if (!empty($object)) {
|
| 90 |
$access = module_invoke_all('subscriptions', 'access', $load_function, $load_args, $object);
|
| 91 |
// One FALSE vote is enough to deny. Also, we need a non-empty array.
|
| 92 |
$allow = !empty($access) && array_search(FALSE, $access) === FALSE;
|
| 93 |
$loaded_objects[$user->uid][$load_function][$index] = $allow ? $object : FALSE;
|
| 94 |
}
|
| 95 |
}
|
| 96 |
if ($object = $loaded_objects[$user->uid][$load_function][$index]) {
|
| 97 |
if (!isset($users[$object->uid])) {
|
| 98 |
$users[$object->uid] = user_load(array('uid' => $object->uid));
|
| 99 |
}
|
| 100 |
$sender = $users[$object->uid];
|
| 101 |
$module = $s['module'];
|
| 102 |
$ori_field = $field = $s['field'];
|
| 103 |
$ori_value = $value = $s['value'];
|
| 104 |
if (!isset($fields[$langcode][$module])) {
|
| 105 |
$fields[$langcode][$module] = module_invoke_all('subscriptions', 'fields', $module);
|
| 106 |
}
|
| 107 |
if ($module == 'node' && $field == 'nid' && (!empty($object->_subscriptions_is_updated) || !empty($object->_subscriptions_is_new)) && user_access('subscribe to content types', $user)) {
|
| 108 |
$unlisteds = variable_get('subscriptions_unlisted_content_types', array());
|
| 109 |
if (isset($object->type) && !in_array($object->type, $unlisteds)) {
|
| 110 |
// Convert node-nid to node-type because that can give more specific information.
|
| 111 |
$field = 'type';
|
| 112 |
$value = $object->type;
|
| 113 |
}
|
| 114 |
}
|
| 115 |
$mailvars_function = $fields[$langcode][$module][$field]['mailvars_function'];
|
| 116 |
$mailkey = $fields[$langcode][$module][$field]['mailkey'];
|
| 117 |
if (!is_numeric($value)) {
|
| 118 |
$mailkey .= '-'. $value;
|
| 119 |
}
|
| 120 |
$digest = $s['digest'] > 0 || $s['digest'] == -1 && _subscriptions_get_setting('digest', 0) > 0;
|
| 121 |
if ($digest) {
|
| 122 |
$body_template = subscriptions_mail_template_load(SUBSCRIPTIONS_DIGEST_MAILKEY .'+item', $langcode, 'body', 'DITEM');
|
| 123 |
}
|
| 124 |
else {
|
| 125 |
$body_template = subscriptions_mail_template_load($mailkey, $langcode, 'body', 'BODY');
|
| 126 |
$subject_template = subscriptions_mail_template_load($mailkey, $langcode, 'subject', 'SUBJ');
|
| 127 |
}
|
| 128 |
init_theme();
|
| 129 |
$show_node_info = (isset($object->type) ? theme_get_setting('toggle_node_info_'. $object->type) : TRUE);
|
| 130 |
$base = 'user/'. $s['uid'];
|
| 131 |
$mailvars = array(
|
| 132 |
'!site' => variable_get('site_name', 'drupal'),
|
| 133 |
'!recipient_name' => $s['name'],
|
| 134 |
'!recipient_page' => url($base, array('absolute' => TRUE)),
|
| 135 |
'!sender_name' => ($show_node_info ? ($sender->uid ? $sender->name : variable_get('anonymous', '!sender_name')) : '!sender_name'),
|
| 136 |
'!sender_page' => ($show_node_info && $sender->uid ? url("user/$sender->uid", array('absolute' => TRUE)) : '!sender_page'),
|
| 137 |
'!sender_contact_page' => ($show_node_info ? (empty($sender->contact) ? t('(disabled)') : url("user/$sender->uid/contact", array('absolute' => TRUE))) : '!sender_contact_page'),
|
| 138 |
'!sender_has_contact_page' => ($show_node_info ? (empty($sender->contact) ? 0 : 1) : 0),
|
| 139 |
'!manage_url' => url($base .'/subscriptions', array('absolute' => TRUE)),
|
| 140 |
'!name' => $s['name'],
|
| 141 |
'!subs_type' => $fields[$langcode][$module][$field]['!subs_type'],
|
| 142 |
'!unsubscribe_url' => url("s/del/$module/$ori_field/$ori_value/". $s['author_uid'] .'/'. $s['uid'] .'/'. md5(drupal_get_private_key() . $module . $ori_field . $ori_value . $s['author_uid'] . $s['uid']), array('absolute' => TRUE)),
|
| 143 |
);
|
| 144 |
$mailvars_function($mailvars, $object, $field, $s);
|
| 145 |
$mailvars += module_invoke_all('subscriptions_get_mailvars', $object);
|
| 146 |
if ($digest && !empty($object->_subscriptions_comments) && module_exists('subscriptions_content')) {
|
| 147 |
$digest_comment_template = subscriptions_mail_template_load(SUBSCRIPTIONS_DIGEST_MAILKEY .'+comment', $langcode, 'body', 'DITEMCMT');
|
| 148 |
$mailvars['!comments'] = _subscriptions_content_format_comments($object, $digest_comment_template, '');
|
| 149 |
}
|
| 150 |
$body = strtr(subscriptions_mail_template_preprocess($body_template, $mailvars), $mailvars);
|
| 151 |
if (variable_get('subscriptions_showmailkeys', 0)) {
|
| 152 |
$body .= t('| Mailkey: @mailkey/@langcode', array('@mailkey' => $mailkey, '@langcode' => $langcode)) ."\n";
|
| 153 |
}
|
| 154 |
if ($digest) {
|
| 155 |
$user_mails['bodies'][] = $body;
|
| 156 |
$user_mails['send'] = array(
|
| 157 |
'name' => $s['name'],
|
| 158 |
'mail' => $s['mail'],
|
| 159 |
'from' => $from,
|
| 160 |
'!name' => $mailvars['!name'],
|
| 161 |
'!manage_url' => $mailvars['!manage_url'],
|
| 162 |
);
|
| 163 |
$user_mails['send_intervals'][$s['send_interval']] = $s['send_interval'];
|
| 164 |
}
|
| 165 |
else {
|
| 166 |
$subject = strtr(subscriptions_mail_template_preprocess($subject_template, $mailvars), $mailvars);
|
| 167 |
_subscriptions_mail_send('passthru', $s['name'], $s['mail'], $subject, $body, $from, $s['uid'], array($s['send_interval']));
|
| 168 |
++$single_count;
|
| 169 |
}
|
| 170 |
}
|
| 171 |
}
|
| 172 |
db_query("DELETE FROM {subscriptions_queue} WHERE load_function = '%s' AND load_args = '%s' AND uid = %d", $s['load_function'], $s['load_args'], $s['uid']);
|
| 173 |
|
| 174 |
if ($digest) {
|
| 175 |
// Get next ready queue item for this user.
|
| 176 |
$result = db_query_range('SELECT * FROM {subscriptions_queue} WHERE uid = %d AND last_sent + send_interval < %d ORDER BY sqid', $user->uid, time(), 0, 1);
|
| 177 |
if (!($s = db_fetch_array($result))) {
|
| 178 |
// No more ready queue items for this user, finish off this digest.
|
| 179 |
$s = $user_mails['send'];
|
| 180 |
$subject_template = subscriptions_mail_template_load(SUBSCRIPTIONS_DIGEST_MAILKEY, $langcode, 'subject', 'DSUBJ');
|
| 181 |
$body_template = subscriptions_mail_template_load(SUBSCRIPTIONS_DIGEST_MAILKEY, $langcode, 'body', 'DBODY');
|
| 182 |
$separator = subscriptions_mail_template_load(SUBSCRIPTIONS_DIGEST_MAILKEY .'+item', $langcode, 'subject', 'SEP');
|
| 183 |
$mailvars['!bodies'] = implode($separator, $user_mails['bodies']);
|
| 184 |
$mailvars['!name'] = $s['!name'];
|
| 185 |
$mailvars['!manage_url'] = $s['!manage_url'];
|
| 186 |
$subject = strtr(subscriptions_mail_template_preprocess($subject_template, $mailvars), $mailvars);
|
| 187 |
$body = strtr(subscriptions_mail_template_preprocess($body_template, $mailvars), $mailvars);
|
| 188 |
_subscriptions_mail_send(SUBSCRIPTIONS_DIGEST_MAILKEY, $s['name'], $s['mail'], $subject, $body, $s['from'], $user->uid, $user_mails['send_intervals']);
|
| 189 |
++$digest_count;
|
| 190 |
$digest = false;
|
| 191 |
}
|
| 192 |
}
|
| 193 |
} while ( $digest );
|
| 194 |
|
| 195 |
$user = $saved_user;
|
| 196 |
$language = $saved_language;
|
| 197 |
session_save_session(TRUE);
|
| 198 |
}
|
| 199 |
|
| 200 |
if ($single_count + $digest_count > 0) {
|
| 201 |
$current_seconds = timer_read('page')/1000;
|
| 202 |
$watchdog = 'watchdog'; // keep potx from translating 'cron'
|
| 203 |
$watchdog('cron', '!Module sent !single_count single and !digest_count digest notifications in !used_seconds of !total_seconds seconds; !remaining_items queue items remaining, !remaining_seconds seconds left for other cron client modules.', array(
|
| 204 |
'!Module' => 'Subscriptions',
|
| 205 |
'!single_count' => $single_count,
|
| 206 |
'!digest_count' => $digest_count,
|
| 207 |
'!used_seconds' => round($current_seconds - $lost_seconds),
|
| 208 |
'!total_seconds' => $total_seconds,
|
| 209 |
'!remaining_items' => db_result(db_query("SELECT COUNT(*) FROM {subscriptions_queue} WHERE last_sent + send_interval < %d", time())),
|
| 210 |
'!remaining_seconds' => round($total_seconds - $current_seconds),
|
| 211 |
));
|
| 212 |
}
|
| 213 |
}
|
| 214 |
|
| 215 |
/**
|
| 216 |
* Implementation of hook_mail().
|
| 217 |
*/
|
| 218 |
function subscriptions_mail_mail($key, &$message, $params) {
|
| 219 |
global $base_url;
|
| 220 |
|
| 221 |
$url = parse_url($base_url);
|
| 222 |
$list_id = variable_get('site_name', '') .' '. t('Subscriptions') .' <subscriptions.'. $url['host'] .'>';
|
| 223 |
|
| 224 |
$message['subject'] = $params['context']['subject'];
|
| 225 |
$message['body'] = $params['context']['message'];
|
| 226 |
$message['headers']['List-Id'] = $list_id;
|
| 227 |
}
|
| 228 |
|
| 229 |
|
| 230 |
/**
|
| 231 |
* Send the notification by mail.
|
| 232 |
*/
|
| 233 |
function _subscriptions_mail_send($mailkey, $name, $to, $subject, $body, $from, $uid, $send_intervals) {
|
| 234 |
global $user;
|
| 235 |
|
| 236 |
$mail_success = drupal_mail('subscriptions_mail', $mailkey, $to, user_preferred_language($user), array(
|
| 237 |
'account' => $user,
|
| 238 |
'object' => NULL,
|
| 239 |
'context' => array(
|
| 240 |
'recipient' => $to,
|
| 241 |
'subject' => $subject,
|
| 242 |
'message' => $body,
|
| 243 |
),
|
| 244 |
), $from, TRUE);
|
| 245 |
|
| 246 |
$watchdog_params = array('@name' => $name, '@to' => "<$to>");
|
| 247 |
if (!empty($mail_success['result'])) {
|
| 248 |
if (variable_get('subscriptions_watchgood', 1)) {
|
| 249 |
watchdog('subscriptions', 'notification for @name at @to', $watchdog_params);
|
| 250 |
}
|
| 251 |
foreach ($send_intervals as $send_interval) {
|
| 252 |
db_query("UPDATE {subscriptions_last_sent} SET last_sent = %d WHERE uid = %d AND send_interval = %d", time(), $uid, $send_interval);
|
| 253 |
if (!db_affected_rows()) {
|
| 254 |
@db_query("INSERT INTO {subscriptions_last_sent} (uid, send_interval, last_sent) VALUES(%d, %d, %d)", $uid, $send_interval, time());
|
| 255 |
}
|
| 256 |
}
|
| 257 |
}
|
| 258 |
else {
|
| 259 |
watchdog('subscriptions', 'error mailing notification for @name at @to', $watchdog_params, WATCHDOG_ERROR);
|
| 260 |
}
|
| 261 |
}
|
| 262 |
|
| 263 |
/**
|
| 264 |
* Preprocess a mail template (subject or body), detecting conditional clauses
|
| 265 |
* that conform to a prescribed syntax
|
| 266 |
*
|
| 267 |
* @param string $template
|
| 268 |
* the template for preprocessing
|
| 269 |
* @param array $mailvars
|
| 270 |
* an associatvie array of currently existing variables that are to be
|
| 271 |
* interpolated into the template later , and which can be used by this
|
| 272 |
* function for preprocessing
|
| 273 |
*
|
| 274 |
* This function allows the administrator to specify ternary-type conditions
|
| 275 |
* to determine what text is used in a mail in a particular situation, using
|
| 276 |
* the variables that are currently available for that mail for reference.
|
| 277 |
* The syntax is standard PHP/C-style ternary syntax, but only allows the
|
| 278 |
* "==" and "!=":
|
| 279 |
* {{!variable_name==sometext?text for true condition:text for false condition}}
|
| 280 |
*
|
| 281 |
* sometext must not contain a question mark, and the true text no colon.
|
| 282 |
*/
|
| 283 |
function subscriptions_mail_template_preprocess($template, $mailvars) {
|
| 284 |
preg_match_all('/{{(?P<condition>[^?]+?)\?(?P<true>[^:]*?):(?P<false>[^\]]*?)}}/', $template, $conditions);
|
| 285 |
|
| 286 |
// locate the actual operators/operand for each
|
| 287 |
$replacement = '';
|
| 288 |
foreach ($conditions[0] as $k => $v) {
|
| 289 |
preg_match('/(?P<operand_1>!.+)\s*(?P<operator>==|!=)\s*(?P<operand_2>.+)/', $conditions['condition'][$k], $matches);
|
| 290 |
$operand1 = (isset($mailvars[$matches['operand_1']]) ? $mailvars[$matches['operand_1']] : $matches['operand_1']);
|
| 291 |
if ($matches['operator'] == '==') {
|
| 292 |
$replacement = ($operand1 == $matches['operand_2']) ? $conditions['true'][$k] : $conditions['false'][$k];
|
| 293 |
}
|
| 294 |
elseif ($matches['operator'] == '!=') {
|
| 295 |
$replacement = ($operand1 != $matches['operand_2']) ? $conditions['true'][$k] : $conditions['false'][$k];
|
| 296 |
}
|
| 297 |
else {
|
| 298 |
continue;
|
| 299 |
}
|
| 300 |
// replace the condition with the result of its evalutation
|
| 301 |
$template = str_replace($v, $replacement, $template);
|
| 302 |
}
|
| 303 |
return $template;
|
| 304 |
}
|
| 305 |
|
| 306 |
/**
|
| 307 |
* Implementation of hook_mail_alter().
|
| 308 |
*
|
| 309 |
* Remove any trailing spaces (must run after mail_edit_mail_alter()!).
|
| 310 |
*/
|
| 311 |
function subscriptions_mail_mail_alter(&$message) {
|
| 312 |
$message['body'] = preg_replace('/ +(\r?\n)/', '\\1', $message['body']);
|
| 313 |
}
|
| 314 |
|