WARNING! NEEDS CAREFUL DEPLOYMENT

* Bug 9213: Fixed the plainly broken user_newtalk updating and caching scheme. I tried to keep my changes roughly performance-neutral, but the update on Wikimedia should be watched carefully for performance problems.
* Made UserMailer a class, use the autoloader to load it
* General UserMailer refactoring
* If the user has email-on-newtalk enabled, send them an email for every change, not just the first one before they view the page again.
* Don't add a watchlist entry automatically on change of user talk page
This commit is contained in:
Tim Starling 2007-10-03 08:46:17 +00:00
parent 73ebcdc977
commit 353f203ce2
5 changed files with 336 additions and 277 deletions

View file

@ -241,6 +241,7 @@ function __autoload($className) {
'User' => 'includes/User.php',
'MailAddress' => 'includes/UserMailer.php',
'EmailNotification' => 'includes/UserMailer.php',
'UserMailer' => 'includes/UserMailer.php',
'WatchedItem' => 'includes/WatchedItem.php',
'WebRequest' => 'includes/WebRequest.php',
'WebResponse' => 'includes/WebResponse.php',
@ -382,4 +383,4 @@ function wfLoadAllExtensions() {
require( $file );
}
}
}
}

View file

@ -4,8 +4,6 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point\n" );
}
require_once('UserMailer.php');
/**
* Class to both describe a background job and handle jobs.
*/
@ -290,3 +288,4 @@ abstract class Job {
}
}

View file

@ -221,8 +221,7 @@ class RecentChange
if( $wgUseEnotif ) {
# this would be better as an extension hook
global $wgUser;
include_once( "UserMailer.php" );
$enotif = new EmailNotification();
$enotif = new EmailNotification;
$title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
$enotif->notifyOnPageChange( $wgUser, $title,
$this->mAttribs['rc_timestamp'],
@ -626,3 +625,4 @@ class RecentChange
}
}

View file

@ -206,13 +206,7 @@ class User {
return false;
}
# Save to cache
$data = array();
foreach ( self::$mCacheVars as $name ) {
$data[$name] = $this->$name;
}
$data['mVersion'] = MW_USER_VERSION;
$wgMemc->set( $key, $data );
$this->saveToCache();
} else {
wfDebug( "Got user {$this->mId} from cache\n" );
# Restore from cache
@ -223,6 +217,25 @@ class User {
return true;
}
/**
* Save user data to the shared cache
*/
function saveToCache() {
$this->load();
if ( $this->isAnon() ) {
// Anonymous users are uncached
return;
}
$data = array();
foreach ( self::$mCacheVars as $name ) {
$data[$name] = $this->$name;
}
$data['mVersion'] = MW_USER_VERSION;
$key = wfMemcKey( 'user', 'id', $this->mId );
global $wgMemc;
$wgMemc->set( $key, $data );
}
/**
* Static factory method for creation from username.
*
@ -1196,11 +1209,13 @@ class User {
global $wgMemc;
$key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
$newtalk = $wgMemc->get( $key );
if( $newtalk != "" ) {
if( strval( $newtalk ) !== '' ) {
$this->mNewtalk = (bool)$newtalk;
} else {
$this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName() );
$wgMemc->set( $key, (int)$this->mNewtalk, time() + 1800 );
// Since we are caching this, make sure it is up to date by getting it
// from the master
$this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName(), true );
$wgMemc->set( $key, (int)$this->mNewtalk, 1800 );
}
} else {
$this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
@ -1227,18 +1242,22 @@ class User {
/**
* Perform a user_newtalk check on current slaves; if the memcached data
* is funky we don't want newtalk state to get stuck on save, as that's
* damn annoying.
*
* Perform a user_newtalk check, uncached.
* Use getNewtalk for a cached check.
*
* @param string $field
* @param mixed $id
* @param bool $fromMaster True to fetch from the master, false for a slave
* @return bool
* @private
*/
function checkNewtalk( $field, $id ) {
$dbr = wfGetDB( DB_SLAVE );
$ok = $dbr->selectField( 'user_newtalk', $field,
function checkNewtalk( $field, $id, $fromMaster = false ) {
if ( $fromMaster ) {
$db = wfGetDB( DB_MASTER );
} else {
$db = wfGetDB( DB_SLAVE );
}
$ok = $db->selectField( 'user_newtalk', $field,
array( $field => $id ), __METHOD__ );
return $ok !== false;
}
@ -1250,17 +1269,18 @@ class User {
* @private
*/
function updateNewtalk( $field, $id ) {
if( $this->checkNewtalk( $field, $id ) ) {
wfDebug( __METHOD__." already set ($field, $id), ignoring\n" );
return false;
}
$dbw = wfGetDB( DB_MASTER );
$dbw->insert( 'user_newtalk',
array( $field => $id ),
__METHOD__,
'IGNORE' );
wfDebug( __METHOD__.": set on ($field, $id)\n" );
return true;
if ( $dbw->affectedRows() ) {
wfDebug( __METHOD__.": set on ($field, $id)\n" );
return true;
} else {
wfDebug( __METHOD__." already set ($field, $id)\n" );
return false;
}
}
/**
@ -1270,16 +1290,17 @@ class User {
* @private
*/
function deleteNewtalk( $field, $id ) {
if( !$this->checkNewtalk( $field, $id ) ) {
wfDebug( __METHOD__.": already gone ($field, $id), ignoring\n" );
return false;
}
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user_newtalk',
array( $field => $id ),
__METHOD__ );
wfDebug( __METHOD__.": killed on ($field, $id)\n" );
return true;
if ( $dbw->affectedRows() ) {
wfDebug( __METHOD__.": killed on ($field, $id)\n" );
return true;
} else {
wfDebug( __METHOD__.": already gone ($field, $id)\n" );
return false;
}
}
/**
@ -1301,6 +1322,7 @@ class User {
$field = 'user_id';
$id = $this->getId();
}
global $wgMemc;
if( $val ) {
$changed = $this->updateNewtalk( $field, $id );
@ -1308,20 +1330,13 @@ class User {
$changed = $this->deleteNewtalk( $field, $id );
}
if( $changed ) {
if( $this->isAnon() ) {
// Anons have a separate memcached space, since
// user records aren't kept for them.
global $wgMemc;
$key = wfMemcKey( 'newtalk', 'ip', $val );
$wgMemc->set( $key, $val ? 1 : 0 );
} else {
if( $val ) {
// Make sure the user page is watched, so a notification
// will be sent out if enabled.
$this->addWatch( $this->getTalkPage() );
}
}
if( $this->isAnon() ) {
// Anons have a separate memcached space, since
// user records aren't kept for them.
$key = wfMemcKey( 'newtalk', 'ip', $id );
$wgMemc->set( $key, $val ? 1 : 0, 1800 );
}
if ( $changed ) {
$this->invalidateCache();
}
}
@ -1893,7 +1908,7 @@ class User {
'wl_notificationtimestamp' => NULL
), array( /* WHERE */
'wl_user' => $currentUser
), 'UserMailer::clearAll'
), __METHOD__
);
# we also need to clear here the "you have new message" notification for the own user_talk page
@ -2378,10 +2393,9 @@ class User {
$from = $wgPasswordSender;
}
require_once( 'UserMailer.php' );
$to = new MailAddress( $this );
$sender = new MailAddress( $from );
$error = userMailer( $to, $sender, $subject, $body );
$error = UserMailer::send( $to, $sender, $subject, $body );
if( $error == '' ) {
return true;
@ -2689,3 +2703,4 @@ class User {
}

View file

@ -1,9 +1,5 @@
<?php
/**
* UserMailer.php
* Copyright (C) 2004 Thomas Gries <mail@tgries.de>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -21,16 +17,10 @@
*
* @author <brion@pobox.com>
* @author <mail@tgries.de>
* @author Tim Starling
*
*/
/**
* Converts a string into a valid RFC 822 "phrase", such as is used for the sender name
*/
function wfRFC822Phrase( $phrase ) {
$phrase = strtr( $phrase, array( "\r" => '', "\n" => '', '"' => '' ) );
return '"' . $phrase . '"';
}
/**
* Stores a single person's name and email address.
@ -70,155 +60,184 @@ class MailAddress {
return $this->address;
}
}
}
function send_mail($mailer, $dest, $headers, $body)
{
$mailResult =& $mailer->send($dest, $headers, $body);
# Based on the result return an error string,
if ($mailResult === true) {
return '';
} elseif (is_object($mailResult)) {
wfDebug( "PEAR::Mail failed: " . $mailResult->getMessage() . "\n" );
return $mailResult->getMessage();
} else {
wfDebug( "PEAR::Mail failed, unknown error result\n" );
return 'Mail object return unknown error.';
function __toString() {
return $this->toString();
}
}
/**
* This function will perform a direct (authenticated) login to
* a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
* array of parameters. It requires PEAR:Mail to do that.
* Otherwise it just uses the standard PHP 'mail' function.
*
* @param $to MailAddress: recipient's email
* @param $from MailAddress: sender's email
* @param $subject String: email's subject.
* @param $body String: email's text.
* @param $replyto String: optional reply-to email (default: null).
* Collection of static functions for sending mail
*/
function userMailer( $to, $from, $subject, $body, $replyto=null ) {
global $wgSMTP, $wgOutputEncoding, $wgErrorString, $wgEnotifImpersonal;
global $wgEnotifMaxRecips;
class UserMailer {
/**
* Send mail using a PEAR mailer
*/
protected static function sendWithPear($mailer, $dest, $headers, $body)
{
$mailResult =& $mailer->send($dest, $headers, $body);
if (is_array( $wgSMTP )) {
require_once( 'Mail.php' );
$msgid = str_replace(" ", "_", microtime());
if (function_exists('posix_getpid'))
$msgid .= '.' . posix_getpid();
if (is_array($to)) {
$dest = array();
foreach ($to as $u)
$dest[] = $u->address;
} else
$dest = $to->address;
$headers['From'] = $from->toString();
if ($wgEnotifImpersonal)
$headers['To'] = 'undisclosed-recipients:;';
else
$headers['To'] = $to->toString();
if ( $replyto ) {
$headers['Reply-To'] = $replyto->toString();
}
$headers['Subject'] = wfQuotedPrintable( $subject );
$headers['Date'] = date( 'r' );
$headers['MIME-Version'] = '1.0';
$headers['Content-type'] = 'text/plain; charset='.$wgOutputEncoding;
$headers['Content-transfer-encoding'] = '8bit';
$headers['Message-ID'] = "<$msgid@" . $wgSMTP['IDHost'] . '>'; // FIXME
$headers['X-Mailer'] = 'MediaWiki mailer';
// Create the mail object using the Mail::factory method
$mail_object =& Mail::factory('smtp', $wgSMTP);
if( PEAR::isError( $mail_object ) ) {
wfDebug( "PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n" );
return $mail_object->getMessage();
}
wfDebug( "Sending mail via PEAR::Mail to $dest\n" );
if (is_array($dest)) {
$chunks = array_chunk($dest, $wgEnotifMaxRecips);
foreach ($chunks as $chunk) {
$e = send_mail($mail_object, $chunk, $headers, $body);
if ($e != '')
return $e;
}
} else
return $mail_object->send($dest, $headers, $body);
} else {
# In the following $headers = expression we removed "Reply-To: {$from}\r\n" , because it is treated differently
# (fifth parameter of the PHP mail function, see some lines below)
# Line endings need to be different on Unix and Windows due to
# the bug described at http://trac.wordpress.org/ticket/2603
if ( wfIsWindows() ) {
$body = str_replace( "\n", "\r\n", $body );
$endl = "\r\n";
} else {
$endl = "\n";
}
$headers =
"MIME-Version: 1.0$endl" .
"Content-type: text/plain; charset={$wgOutputEncoding}$endl" .
"Content-Transfer-Encoding: 8bit$endl" .
"X-Mailer: MediaWiki mailer$endl".
'From: ' . $from->toString();
if ($replyto) {
$headers .= "{$endl}Reply-To: " . $replyto->toString();
}
$wgErrorString = '';
set_error_handler( 'mailErrorHandler' );
wfDebug( "Sending mail via internal mail() function\n" );
if (function_exists('mail'))
if (is_array($to))
foreach ($to as $recip)
$sent = mail( $recip->toString(), wfQuotedPrintable( $subject ), $body, $headers );
else
$sent = mail( $to->toString(), wfQuotedPrintable( $subject ), $body, $headers );
else
$wgErrorString = 'PHP is not configured to send mail';
restore_error_handler();
if ( $wgErrorString ) {
wfDebug( "Error sending mail: $wgErrorString\n" );
return $wgErrorString;
} elseif (! $sent) {
//mail function only tells if there's an error
wfDebug( "Error sending mail\n" );
return 'mailer error';
} else {
# Based on the result return an error string,
if ($mailResult === true) {
return '';
} elseif (is_object($mailResult)) {
wfDebug( "PEAR::Mail failed: " . $mailResult->getMessage() . "\n" );
return $mailResult->getMessage();
} else {
wfDebug( "PEAR::Mail failed, unknown error result\n" );
return 'Mail object return unknown error.';
}
}
/**
* This function will perform a direct (authenticated) login to
* a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
* array of parameters. It requires PEAR:Mail to do that.
* Otherwise it just uses the standard PHP 'mail' function.
*
* @param $to MailAddress: recipient's email
* @param $from MailAddress: sender's email
* @param $subject String: email's subject.
* @param $body String: email's text.
* @param $replyto String: optional reply-to email (default: null).
*/
static function send( $to, $from, $subject, $body, $replyto=null ) {
global $wgSMTP, $wgOutputEncoding, $wgErrorString, $wgEnotifImpersonal;
global $wgEnotifMaxRecips;
if ( is_array( $to ) ) {
wfDebug( __METHOD__.': sending mail to ' . implode( ',', $to ) . "\n" );
} else {
wfDebug( __METHOD__.': sending mail to ' . implode( ',', array( $to ) ) . "\n" );
}
if (is_array( $wgSMTP )) {
require_once( 'Mail.php' );
$msgid = str_replace(" ", "_", microtime());
if (function_exists('posix_getpid'))
$msgid .= '.' . posix_getpid();
if (is_array($to)) {
$dest = array();
foreach ($to as $u)
$dest[] = $u->address;
} else
$dest = $to->address;
$headers['From'] = $from->toString();
if ($wgEnotifImpersonal)
$headers['To'] = 'undisclosed-recipients:;';
else
$headers['To'] = $to->toString();
if ( $replyto ) {
$headers['Reply-To'] = $replyto->toString();
}
$headers['Subject'] = wfQuotedPrintable( $subject );
$headers['Date'] = date( 'r' );
$headers['MIME-Version'] = '1.0';
$headers['Content-type'] = 'text/plain; charset='.$wgOutputEncoding;
$headers['Content-transfer-encoding'] = '8bit';
$headers['Message-ID'] = "<$msgid@" . $wgSMTP['IDHost'] . '>'; // FIXME
$headers['X-Mailer'] = 'MediaWiki mailer';
// Create the mail object using the Mail::factory method
$mail_object =& Mail::factory('smtp', $wgSMTP);
if( PEAR::isError( $mail_object ) ) {
wfDebug( "PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n" );
return $mail_object->getMessage();
}
wfDebug( "Sending mail via PEAR::Mail to $dest\n" );
if (is_array($dest)) {
$chunks = array_chunk($dest, $wgEnotifMaxRecips);
foreach ($chunks as $chunk) {
$e = self::sendWithPear($mail_object, $chunk, $headers, $body);
if ($e != '')
return $e;
}
} else
return $mail_object->send($dest, $headers, $body);
} else {
# In the following $headers = expression we removed "Reply-To: {$from}\r\n" , because it is treated differently
# (fifth parameter of the PHP mail function, see some lines below)
# Line endings need to be different on Unix and Windows due to
# the bug described at http://trac.wordpress.org/ticket/2603
if ( wfIsWindows() ) {
$body = str_replace( "\n", "\r\n", $body );
$endl = "\r\n";
} else {
$endl = "\n";
}
$headers =
"MIME-Version: 1.0$endl" .
"Content-type: text/plain; charset={$wgOutputEncoding}$endl" .
"Content-Transfer-Encoding: 8bit$endl" .
"X-Mailer: MediaWiki mailer$endl".
'From: ' . $from->toString();
if ($replyto) {
$headers .= "{$endl}Reply-To: " . $replyto->toString();
}
$wgErrorString = '';
$html_errors = ini_get( 'html_errors' );
ini_set( 'html_errors', '0' );
set_error_handler( array( 'UserMailer', 'errorHandler' ) );
wfDebug( "Sending mail via internal mail() function\n" );
if (function_exists('mail')) {
if (is_array($to)) {
foreach ($to as $recip) {
$sent = mail( $recip->toString(), wfQuotedPrintable( $subject ), $body, $headers );
}
} else {
$sent = mail( $to->toString(), wfQuotedPrintable( $subject ), $body, $headers );
}
} else {
$wgErrorString = 'PHP is not configured to send mail';
}
restore_error_handler();
ini_set( 'html_errors', $html_errors );
if ( $wgErrorString ) {
wfDebug( "Error sending mail: $wgErrorString\n" );
return $wgErrorString;
} elseif (! $sent) {
//mail function only tells if there's an error
wfDebug( "Error sending mail\n" );
return 'mailer error';
} else {
return '';
}
}
}
/**
* Get the mail error message in global $wgErrorString
*
* @param $code Integer: error number
* @param $string String: error message
*/
static function errorHandler( $code, $string ) {
global $wgErrorString;
$wgErrorString = preg_replace( '/^mail\(\)(\s*\[.*?\])?: /', '', $string );
}
/**
* Converts a string into a valid RFC 822 "phrase", such as is used for the sender name
*/
static function rfc822Phrase( $phrase ) {
$phrase = strtr( $phrase, array( "\r" => '', "\n" => '', '"' => '' ) );
return '"' . $phrase . '"';
}
}
/**
* Get the mail error message in global $wgErrorString
*
* @param $code Integer: error number
* @param $string String: error message
*/
function mailErrorHandler( $code, $string ) {
global $wgErrorString;
$wgErrorString = preg_replace( '/^mail\(\)(\s*\[.*?\])?: /', '', $string );
}
/**
* This module processes the email notifications when the current page is
* changed. It looks up the table watchlist to find out which users are watching
@ -245,10 +264,24 @@ class EmailNotification {
*/
var $to, $subject, $body, $replyto, $from;
var $user, $title, $timestamp, $summary, $minorEdit, $oldid;
var $mailTargets = array();
/**@}}*/
function notifyOnPageChange($editor, &$title, $timestamp, $summary, $minorEdit, $oldid = false) {
/**
* Send emails corresponding to the user $editor editing the page $title.
* Also updates wl_notificationtimestamp.
*
* May be deferred via the job queue.
*
* @param $editor User object
* @param $title Title object
* @param $timestamp
* @param $summary
* @param $minorEdit
* @param $oldid (default: false)
*/
function notifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid = false) {
global $wgEnotifUseJobQ;
if( $title->getNamespace() < 0 )
@ -269,23 +302,27 @@ class EmailNotification {
}
/**
* @todo document
/*
* Immediate version of notifyOnPageChange().
*
* Send emails corresponding to the user $editor editing the page $title.
* Also updates wl_notificationtimestamp.
*
* @param $editor User object
* @param $title Title object
* @param $timestamp
* @param $summary
* @param $minorEdit
* @param $oldid (default: false)
*/
function actuallyNotifyOnPageChange($editor, &$title, $timestamp, $summary, $minorEdit, $oldid=false) {
function actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid=false) {
# we use $wgEmergencyContact as sender's address
global $wgEnotifWatchlist;
global $wgEnotifMinorEdits, $wgEnotifUserTalk, $wgShowUpdatedMarker;
global $wgEnotifImpersonal;
$fname = 'UserMailer::notifyOnPageChange';
wfProfileIn( $fname );
wfProfileIn( __METHOD__ );
# The following code is only run, if several conditions are met:
# 1. EmailNotification for pages (other than user_talk pages) must be enabled
@ -295,37 +332,37 @@ class EmailNotification {
$enotifusertalkpage = ($isUserTalkPage && $wgEnotifUserTalk);
$enotifwatchlistpage = $wgEnotifWatchlist;
$this->title =& $title;
$this->title = $title;
$this->timestamp = $timestamp;
$this->summary = $summary;
$this->minorEdit = $minorEdit;
$this->oldid = $oldid;
$this->composeCommonMailtext($editor);
$impersonals = array();
$userTalkId = false;
if ( (!$minorEdit || $wgEnotifMinorEdits) ) {
if( $wgEnotifWatchlist ) {
if ( $wgEnotifUserTalk && $isUserTalkPage ) {
$targetUser = User::newFromName( $title->getText() );
if ( !$targetUser || $targetUser->isAnon() ) {
wfDebug( __METHOD__.": user talk page edited, but user does not exist\n" );
} elseif ( $targetUser->getId() == $editor->getId() ) {
wfDebug( __METHOD__.": user edited their own talk page, no notification sent\n" );
} else {
$this->compose( $targetUser );
$userTalkId = $targetUser->getId();
}
}
if ( $wgEnotifWatchlist ) {
// Send updates to watchers other than the current editor
$userCondition = 'wl_user <> ' . intval( $editor->getId() );
} elseif( $wgEnotifUserTalk && $title->getNamespace() == NS_USER_TALK ) {
$targetUser = User::newFromName( $title->getText() );
if( is_null( $targetUser ) ) {
wfDebug( "$fname: user-talk-only mode; no such user\n" );
$userCondition = false;
} elseif( $targetUser->getId() == $editor->getId() ) {
wfDebug( "$fname: user-talk-only mode; editor is target user\n" );
$userCondition = false;
} else {
// Don't notify anyone other than the owner of the talk page
$userCondition = 'wl_user = ' . intval( $targetUser->getId() );
if ( $userTalkId !== false ) {
// Already sent an email to this person
$userCondition .= ' AND wl_user <> ' . intval( $userTalkId );
}
} else {
// Notifications disabled
$userCondition = false;
}
if( $userCondition ) {
$dbr = wfGetDB( DB_MASTER );
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'watchlist', array( 'wl_user' ),
array(
@ -333,70 +370,44 @@ class EmailNotification {
'wl_namespace' => $title->getNamespace(),
$userCondition,
'wl_notificationtimestamp IS NULL',
), $fname );
), __METHOD__ );
# if anyone is watching ... set up the email message text which is
# common for all receipients ...
if ( $dbr->numRows( $res ) > 0 ) {
$watchingUser = new User();
# ... now do for all watching users ... if the options fit
for ($i = 1; $i <= $dbr->numRows( $res ); $i++) {
$wuser = $dbr->fetchObject( $res );
$watchingUser->setID($wuser->wl_user);
if ( ( ( $enotifwatchlistpage
&& $watchingUser->getOption('enotifwatchlistpages') )
|| ( $enotifusertalkpage
&& $watchingUser->getOption('enotifusertalkpages')
&& $title->equals( $watchingUser->getTalkPage() ) ) )
&& ( !$minorEdit
|| ( $wgEnotifMinorEdits
&& $watchingUser->getOption('enotifminoredits') ) )
&& ( $watchingUser->isEmailConfirmed() ) ) {
# ... adjust remaining text and page edit time placeholders
# which needs to be personalized for each user
if ($wgEnotifImpersonal)
$impersonals[] = $watchingUser;
else
$this->composeAndSendPersonalisedMail( $watchingUser );
} # if the watching user has an email address in the preferences
foreach ( $res as $row ) {
$watchingUser = User::newFromId( $row->wl_user );
if ( $watchingUser->getOption( 'enotifwatchlistpages' ) &&
( !$minorEdit || $watchingUser->getOption('enotifminoredits') ) &&
$watchingUser->isEmailConfirmed() )
{
$this->compose( $watchingUser );
}
}
} # if anyone is watching
} # if $wgEnotifWatchlist = true
}
}
global $wgUsersNotifedOnAllChanges;
foreach ( $wgUsersNotifedOnAllChanges as $name ) {
$user = User::newFromName( $name );
if ($wgEnotifImpersonal)
$impersonals[] = $user;
else
$this->composeAndSendPersonalisedMail( $user );
$this->compose( $user );
}
$this->composeAndSendImpersonalMail($impersonals);
$this->sendMails();
if ( $wgShowUpdatedMarker || $wgEnotifWatchlist ) {
# mark the changed watch-listed page with a timestamp, so that the page is
# listed with an "updated since your last visit" icon in the watch list, ...
$dbw = wfGetDB( DB_MASTER );
$success = $dbw->update( 'watchlist',
$dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => $dbw->timestamp($timestamp)
), array( /* WHERE */
'wl_title' => $title->getDBkey(),
'wl_namespace' => $title->getNamespace(),
'wl_notificationtimestamp IS NULL'
), 'UserMailer::NotifyOnChange'
), __METHOD__
);
# FIXME what do we do on failure ?
}
wfProfileOut( $fname );
wfProfileOut( __METHOD__ );
} # function NotifyOnChange
/**
@ -497,6 +508,31 @@ class EmailNotification {
$this->body = $body;
}
/**
* Compose a mail to a given user and either queue it for sending, or send it now,
* depending on settings.
*
* Call sendMails() to send any mails that were queued.
*/
function compose( $user ) {
global $wgEnotifImpersonal;
if ( $wgEnotifImpersonal ) {
$this->mailTargets[] = new MailAddress( $user );
} else {
$this->sendPersonalised( $user );
}
}
/**
* Send any queued mails
*/
function sendMails() {
global $wgEnotifImpersonal;
if ( $wgEnotifImpersonal ) {
$this->sendImpersonal( $this->mailTargets );
}
}
/**
* Does the per-user customizations to a notification e-mail (name,
* timestamp in proper timezone, etc) and sends it out.
@ -507,7 +543,7 @@ class EmailNotification {
* @return bool
* @private
*/
function composeAndSendPersonalisedMail( $watchingUser ) {
function sendPersonalised( $watchingUser ) {
global $wgLang;
// From the PHP manual:
// Note: The to parameter cannot be an address in the form of "Something <someone@example.com>".
@ -524,23 +560,19 @@ class EmailNotification {
$wgLang->timeanddate( $this->timestamp, true, false, $timecorrection ),
$body);
return userMailer($to, $this->from, $this->subject, $body, $this->replyto);
return UserMailer::send($to, $this->from, $this->subject, $body, $this->replyto);
}
/**
* Same as composeAndSendPersonalisedMail but does impersonal mail
* suitable for bulk mailing. Takes an array of users.
* Same as sendPersonalised but does impersonal mail suitable for bulk
* mailing. Takes an array of MailAddress objects.
*/
function composeAndSendImpersonalMail($users) {
function sendImpersonal( $addresses ) {
global $wgLang;
if (empty($users))
if (empty($addresses))
return;
$to = array();
foreach ($users as $user)
$to[] = new MailAddress($user);
$body = str_replace(
array( '$WATCHINGUSERNAME',
'$PAGEEDITDATE'),
@ -548,8 +580,20 @@ class EmailNotification {
$wgLang->timeanddate($this->timestamp, true, false, false)),
$this->body);
return userMailer($to, $this->from, $this->subject, $body, $this->replyto);
return UserMailer::send($addresses, $this->from, $this->subject, $body, $this->replyto);
}
} # end of class EmailNotification
/**
* Backwards compatibility functions
*/
function wfRFC822Phrase( $s ) {
return UserMailer::rfc822Phrase( $s );
}
function userMailer( $to, $from, $subject, $body, $replyto=null ) {
return UserMailer::send( $to, $from, $subject, $body, $replyto );
}