2004-02-28 07:08:13 +00:00
< ? php
2005-01-27 18:19:16 +00:00
/**
* 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
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
* http :// www . gnu . org / copyleft / gpl . html
*
* @ author < brion @ pobox . com >
* @ author < mail @ tgries . de >
*
* @ package MediaWiki
*/
2004-12-18 03:47:11 +00:00
2004-09-02 23:28:24 +00:00
/**
* Provide mail capabilities
2005-01-27 18:19:16 +00:00
* @ param string $string ? ? ? ?
*
* The function takes formats like " name <emailaddr> " into account , for which
* the partial string " name " will be quoted - printable converted but not
* " <emailaddr> " .
2004-09-02 23:28:24 +00:00
*
2005-01-27 18:19:16 +00:00
* The php mail () function does not accept the email address partial string to
* be quotedprintable , it does not accept inputs as
* quotedprintable ( " name <emailaddr> " ) .
*
* case 1 :
* input : " name <emailaddr> rest "
* output : " quoted-printable(name) <emailaddr> "
*
* case 2 : ( should not happen )
* input : " <emailaddr> rest "
* output : " <emailaddr> "
*
* case 3 : ( should not happen )
* input : " name "
* output : " quoted-printable(name) "
2004-09-02 23:28:24 +00:00
*/
2004-12-18 03:47:11 +00:00
function wfQuotedPrintable_name_and_emailaddr ( $string ) {
/* do not quote printable for email address string <emailaddr>, but only for the (leading) string, usually the name */
preg_match ( '/^([^<]*)?(<([A-z0-9_.-]+([A-z0-9_.-]+)*\@[A-z0-9_-]+([A-z0-9_.-]+)*([A-z.]{2,})+)>)?$/' , $string , $part );
if ( ! isset ( $part [ 1 ]) ) return $part [ 2 ];
if ( ! isset ( $part [ 2 ]) ) return wfQuotedprintable ( $part [ 1 ]);
return wfQuotedprintable ( $part [ 1 ]) . $part [ 2 ] ;
}
2004-09-02 23:28:24 +00:00
/**
* 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 string $to recipient ' s email
* @ param string $from sender ' s email
* @ param string $subject email ' s subject
* @ param string $body email ' s text
2005-01-27 18:19:16 +00:00
* @ param string $replyto optional reply - to email ( default : false )
2004-09-02 23:28:24 +00:00
*/
2004-12-18 03:47:11 +00:00
function userMailer ( $to , $from , $subject , $body , $replyto = false ) {
global $wgUser , $wgSMTP , $wgOutputEncoding , $wgErrorString , $wgEmergencyContact ;
2004-02-27 12:48:07 +00:00
2004-12-18 03:47:11 +00:00
$qto = wfQuotedPrintable_name_and_emailaddr ( $to );
2004-02-27 12:48:07 +00:00
2004-12-18 03:47:11 +00:00
if ( is_array ( $wgSMTP )) {
2004-08-22 17:24:50 +00:00
require_once ( 'Mail.php' );
2004-02-27 12:48:07 +00:00
$timestamp = time ();
2004-08-22 17:24:50 +00:00
$headers [ 'From' ] = $from ;
2004-05-20 03:04:14 +00:00
/* removing to : field as it should be set by the send () function below
UNTESTED - Hashar */
2004-12-18 03:47:11 +00:00
// $headers['To'] = $qto;
/* Reply - To :
UNTESTED - Tom Gries */
$headers [ 'Reply-To' ] = $replyto ;
2004-08-22 23:32:05 +00:00
$headers [ 'Subject' ] = $subject ;
2004-08-22 17:24:50 +00:00
$headers [ 'MIME-Version' ] = '1.0' ;
$headers [ 'Content-type' ] = 'text/plain; charset=' . $wgOutputEncoding ;
$headers [ 'Content-transfer-encoding' ] = '8bit' ;
$headers [ 'Message-ID' ] = " < { $timestamp } " . $wgUser -> getName () . '@' . $wgSMTP [ 'IDHost' ] . '>' ;
2004-12-18 03:47:11 +00:00
$headers [ 'X-Mailer' ] = 'MediaWiki mailer' ;
2004-02-27 12:48:07 +00:00
// Create the mail object using the Mail::factory method
2004-08-22 17:24:50 +00:00
$mail_object =& Mail :: factory ( 'smtp' , $wgSMTP );
2004-02-27 12:48:07 +00:00
$mailResult =& $mail_object -> send ( $to , $headers , $body );
# Based on the result return an error string,
if ( $mailResult === true )
2004-08-22 17:24:50 +00:00
return '' ;
2004-02-27 12:48:07 +00:00
else if ( is_object ( $mailResult ))
return $mailResult -> getMessage ();
else
2004-08-22 17:24:50 +00:00
return 'Mail object return unknown error.' ;
2004-12-18 03:47:11 +00:00
} 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)
2004-02-27 12:48:07 +00:00
$headers =
2004-08-15 23:48:39 +00:00
" MIME-Version: 1.0 \n " .
" Content-type: text/plain; charset= { $wgOutputEncoding } \n " .
2004-12-18 03:47:11 +00:00
" Content-Transfer-Encoding: 8bit \n " .
" X-Mailer: MediaWiki mailer \n " .
'From:' . wfQuotedPrintable_name_and_emailaddr ( $from ) . " \n " ;
if ( $replyto ) {
$headers .= 'Reply-To: ' . wfQuotedPrintable_name_and_emailaddr ( $replyto ) . " \n " ;
}
2004-04-01 13:05:20 +00:00
2004-08-22 17:24:50 +00:00
$wgErrorString = '' ;
set_error_handler ( 'mailErrorHandler' );
2004-12-18 03:47:11 +00:00
# added -f parameter, see PHP manual for the fifth parameter when using the mail function
mail ( wfQuotedPrintable_name_and_emailaddr ( $to ), $subject , $body , $headers , " -f { $wgEmergencyContact } \n " );
2004-04-01 13:05:20 +00:00
restore_error_handler ();
return $wgErrorString ;
2004-02-27 12:48:07 +00:00
}
}
2004-09-02 23:28:24 +00:00
/**
2005-01-27 19:51:47 +00:00
* @ todo document
2004-09-02 23:28:24 +00:00
*/
2004-04-01 13:05:20 +00:00
function mailErrorHandler ( $code , $string ) {
global $wgErrorString ;
$wgErrorString = preg_replace ( " /^mail \ ( \ ): / " , " " , $string );
}
2004-12-18 03:47:11 +00:00
2004-12-18 10:23:54 +00:00
/**
2005-01-27 18:19:16 +00:00
* 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
* that page .
2004-12-18 10:23:54 +00:00
*
2005-01-27 18:19:16 +00:00
* The current implementation sends independent emails to each watching user for
* the following reason :
2004-12-18 10:23:54 +00:00
*
2005-01-27 18:19:16 +00:00
* - Each watching user will be notified about the page edit time expressed in
* his / her local time ( UTC is shown additionally ) . To achieve this , we need to
* find the individual timeoffset of each watching user from the preferences ..
2004-12-18 10:23:54 +00:00
*
2005-01-27 18:19:16 +00:00
* Suggested improvement to slack down the number of sent emails : We could think
* of sending out bulk mails ( bcc : user1 , user2 ... ) for all these users having the
* same timeoffset in their preferences .
2004-12-18 10:23:54 +00:00
*
2005-01-27 18:19:16 +00:00
* Visit the documentation pages under http :// meta . wikipedia . com / Enotif
*
* @ package MediaWiki
*
2004-12-18 10:23:54 +00:00
*/
class EmailNotification {
2005-01-27 18:19:16 +00:00
/** #@+
* @ access private
*/
2004-12-18 10:23:54 +00:00
var $to , $subject , $body , $replyto , $from ;
2005-01-27 18:19:16 +00:00
/**#@-*/
/**
* @ todo document
* @ param $currentUser
* @ param $currentPage
* @ param $currentNs
* @ param $timestamp
* @ param $currentSummary
* @ param $currentMinorEdit
* @ param $oldid ( default : false )
*/
2004-12-18 10:23:54 +00:00
function NotifyOnPageChange ( $currentUser , $currentPage , $currentNs , $timestamp , $currentSummary , $currentMinorEdit , $oldid = false ) {
# we use $wgEmergencyContact as sender's address
global $wgUser , $wgLang , $wgEmergencyContact ;
global $wgEmailNotificationForWatchlistPages , $wgEmailNotificationForMinorEdits ;
global $wgEmailNotificationSystembeep , $wgEmailNotificationForUserTalkPages ;
global $wgEmailNotificationRevealPageEditorAddress ;
global $wgEmailNotificationMailsSentFromPageEditor ;
global $wgEmailAuthentication ;
global $beeped ;
# The following code is only run, if several conditions are met:
# 1. EmailNotification for pages (other than user_talk pages) must be enabled
# 2. minor edits (changes) are only regarded if the global flag indicates so
$isUserTalkPage = ( $currentNs == NS_USER_TALK );
$enotifusertalkpage = ( $isUserTalkPage && $wgEmailNotificationForUserTalkPages );
$enotifwatchlistpage = ( ! $isUserTalkPage && $wgEmailNotificationForWatchlistPages );
if ( ( $enotifusertalkpage || $enotifwatchlistpage )
&& ( ! $currentMinorEdit || $wgEmailNotificationForMinorEdits ) ) {
$dbr =& wfGetDB ( DB_MASTER );
extract ( $dbr -> tableNames ( 'watchlist' ) );
$sql = " SELECT wl_user FROM $watchlist
WHERE wl_title = '" . $dbr->strencode($currentPage)."' AND wl_namespace = " . $currentNs .
" AND wl_user <> " . $currentUser . " AND wl_notificationtimestamp <= 1 " ;
$res = $dbr -> query ( $sql , 'UserMailer::NotifyOnChange' );
2005-01-27 18:19:16 +00:00
# if anyone is watching ... set up the email message text which is
# common for all receipients ...
if ( $dbr -> numRows ( $res ) > 0 ) {
2004-12-18 10:23:54 +00:00
# This is a switch for one beep on the server when sending notification mails
$beeped = false ;
2005-01-27 18:19:16 +00:00
2004-12-18 10:23:54 +00:00
$article -> mTimestamp = $timestamp ;
$article -> mSummary = $currentSummary ;
$article -> mMinorEdit = $currentMinorEdit ;
$article -> mNamespace = $currentNs ;
$article -> mTitle = $currentPage ;
$article -> mOldid = $oldid ;
$mail = $this -> composeCommonMailtext ( $wgUser , $article );
$watchingUser = new User ();
2005-01-27 18:19:16 +00:00
# ... now do for all watching users ... if the options fit
for ( $i = 1 ; $i <= $dbr -> numRows ( $res ); $i ++ ) {
2004-12-18 10:23:54 +00:00
$wuser = $dbr -> fetchObject ( $res );
$watchingUser -> setID ( $wuser -> wl_user );
if ( ( $enotifwatchlistpage && $watchingUser -> getOption ( 'enotifwatchlistpages' ) ) ||
( $enotifusertalkpage && $watchingUser -> getOption ( 'enotifusertalkpages' ) )
&& ( ! $currentMinorEdit || ( $wgEmailNotificationForMinorEdits && $watchingUser -> getOption ( 'enotifminoredits' ) ) )
&& ( $watchingUser -> getEmail () != '' )
&& ( ! $wgEmailAuthentication || ( $watchingUser -> getEmailAuthenticationtimestamp () != 0 ) ) ) {
# ... adjust remaining text and page edit time placeholders
# which needs to be personalized for each user
$sent = $this -> composeAndSendPersonalisedMail ( $watchingUser , $mail , $article );
/* the beep here beeps once when a watched-listed page is changed */
if ( $sent && ! $beeped && ( $wgEmailNotificationSystembeep != '' ) ) {
$last_line = system ( $wgEmailNotificationSystembeep );
$beeped = true ;
}
} # if the watching user has an email address in the preferences
# 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, ...
# ... no matter, if the watching user has or has not indicated an email address in his/her preferences.
# We memorise the event of sending out a notification and use this as a flag to suppress further mails for changes on the same page for that watching user
$dbw =& wfGetDB ( DB_MASTER );
$succes = $dbw -> update ( 'watchlist' ,
array ( /* SET */
'wl_notificationtimestamp' => $article -> mTimestamp
), array ( /* WHERE */
'wl_title' => $currentPage ,
'wl_namespace' => $currentNs ,
'wl_user' => $wuser -> wl_user
), 'UserMailer::NotifyOnChange'
);
} # for every watching user
} # if anyone is watching
} # if $wgEmailNotificationForWatchlistPages = true
} # function NotifyOnChange
/**
* @ param User $pageeditorUser
* @ param Article $article
* @ access private
*/
function composeCommonMailtext ( $pageeditorUser , $article ) {
global $wgLang , $wgEmergencyContact ;
global $wgEmailNotificationRevealPageEditorAddress ;
global $wgEmailNotificationMailsSentFromPageEditor ;
global $wgNoReplyAddress ;
$summary = ( $article -> mSummary == '' ) ? ' - ' : $article -> mSummary ;
$medit = ( $article -> mMinorEdit ) ? wfMsg ( 'minoredit' ) : '' ;
# You as the WikiAdmin and Sysops can make use of plenty of
# named variables when composing your notification emails while
# simply editing the Meta pages
$to = wfMsg ( 'email_notification_to' );
$subject = wfMsg ( 'email_notification_subject' );
$body = wfMsg ( 'email_notification_body' );
$from = '' ; /* fail safe */
$replyto = '' ; /* fail safe */
$keys = array ();
# regarding the use of oldid as an indicator for the last visited version, see also
# http://bugzilla.wikipeda.org/show_bug.cgi?id=603 "Delete + undelete cycle doesn't preserve old_id"
# However, in the case of a new page which is already watched, we have no previous version to compare
if ( $article -> mOldid ) {
$keys [ '$NEWPAGE' ] = wfMsg ( 'email_notification_lastvisitedrevisiontext' );
$keys [ '$OLDID' ] = $article -> mOldid ;
2004-12-18 03:47:11 +00:00
} else {
2004-12-18 10:23:54 +00:00
$keys [ '$NEWPAGE' ] = wfMsg ( 'email_notification_newpagetext' );
# clear $OLDID placeholder in the message template
$keys [ '$OLDID' ] = '' ;
2004-12-18 03:47:11 +00:00
}
2005-01-19 22:55:06 +00:00
$body = strtr ( $body , $keys );
2004-12-18 10:23:54 +00:00
$pagetitle = $article -> mTitle ;
if ( $article -> mNamespace != NS_MAIN ) {
$pagetitle = $wgLang -> getNsText ( $article -> mNamespace ) . ':' . $pagetitle ;
}
2005-03-26 01:21:01 +00:00
$subject = str_replace ( '$PAGETITLE' , str_replace ( '_' , ' ' , $pagetitle ) , $subject );
2004-12-18 10:23:54 +00:00
$keys [ '%24PAGETITLE_RAWURL' ] = wfUrlencode ( $pagetitle );
$keys [ '$PAGETITLE_RAWURL' ] = wfUrlencode ( $pagetitle );
$keys [ '%24PAGETITLE' ] = $pagetitle ; # needed for the {{localurl:$PAGETITLE}} in the messagetext, "$" appears here as "%24"
$keys [ '$PAGETITLE' ] = $pagetitle ;
$keys [ '$PAGETIMESTAMP' ] = $article -> mTimestamp ; # this is the raw internal timestamp - can be useful, too
2005-04-10 18:42:16 +00:00
$keys [ '$PAGEEDITDATEUTC' ] = $wgLang -> timeanddate ( $article -> mTimestamp , false , false , false );
2004-12-18 10:23:54 +00:00
$keys [ '$PAGEMINOREDIT' ] = $medit ;
$keys [ '$PAGESUMMARY' ] = $summary ;
2004-12-18 03:47:11 +00:00
2004-12-18 10:23:54 +00:00
# Reveal the page editor's address as REPLY-TO address only if
# the user has not opted-out and the option is enabled at the
# global configuration level.
2005-03-26 01:21:01 +00:00
$name = $pageeditorUser -> getName ();
2004-12-18 10:23:54 +00:00
$adminAddress = 'WikiAdmin <' . $wgEmergencyContact . '>' ;
2005-03-26 01:21:01 +00:00
$editorAddress = $name . ' <' . $pageeditorUser -> getEmail () . '>' ;
2004-12-18 10:23:54 +00:00
if ( $wgEmailNotificationRevealPageEditorAddress
&& ( $pageeditorUser -> getEmail () != '' )
&& $pageeditorUser -> getOption ( 'enotifrevealaddr' ) ) {
if ( $wgEmailNotificationMailsSentFromPageEditor ) {
$from = $editorAddress ;
} else {
$from = $adminAddress ;
$replyto = $editorAddress ;
}
$keys [ '$PAGEEDITORNAMEANDEMAILADDR' ] = $editorAddress ;
} else {
$from = $adminAddress ;
$replyto = $wgNoReplyAddress ;
$keys [ '$PAGEEDITORNAMEANDEMAILADDR' ] = $replyto ;
}
2005-03-26 01:21:01 +00:00
if ( $pageeditorUser -> isIP ( $name ) ) {
2004-12-18 10:23:54 +00:00
#real anon (user:xxx.xxx.xxx.xxx)
$anon = $name . ' (anonymous user)' ;
$anonUrl = wfUrlencode ( $name ) . ' (anonymous user)' ;
2005-03-26 01:21:01 +00:00
$subject = str_replace ( '$PAGEEDITOR' , 'anonymous user ' . $name , $subject );
2004-12-18 10:23:54 +00:00
$keys [ '$PAGEEDITOR_RAWURL' ] = $anonUrl ;
$keys [ '%24PAGEEDITOR_RAWURL' ] = $anonUrl ;
$keys [ '%24PAGEEDITORE' ] = $anon ;
$keys [ '$PAGEEDITORE' ] = $anon ;
$keys [ '$PAGEEDITOR' ] = 'anonymous user ' . $name ;
} else {
2005-03-26 01:21:01 +00:00
$subject = str_replace ( '$PAGEEDITOR' , $name , $subject );
2004-12-18 10:23:54 +00:00
$keys [ '$PAGEEDITOR_RAWURL' ] = wfUrlencode ( $name );
$keys [ '%24PAGEEDITOR_RAWURL' ] = wfUrlencode ( $name );
$keys [ '%24PAGEEDITORE' ] = $pageeditorUser -> getTitleKey ();
$keys [ '$PAGEEDITORE' ] = $pageeditorUser -> getTitleKey ();
2005-03-26 01:21:01 +00:00
$keys [ '$PAGEEDITOR' ] = $name ;
2004-12-18 10:23:54 +00:00
}
$body = strtr ( $body , $keys );
# now save this as the constant user-independent part of the message
$this -> to = $to ;
$this -> from = $from ;
$this -> replyto = $replyto ;
2005-03-26 01:21:01 +00:00
$this -> subject = wfQuotedprintable ( $subject );
2004-12-18 10:23:54 +00:00
$this -> body = $body ;
return $this ;
2004-12-18 03:47:11 +00:00
}
2004-12-18 10:23:54 +00:00
/**
* Does the per - user customizations to a notification e - mail ( name ,
* timestamp in proper timezone , etc ) and sends it out .
* Returns true if the mail was sent successfully .
*
* @ param User $watchingUser
* @ param object $mail
* @ param Article $article
* @ return bool
* @ access private
*/
function composeAndSendPersonalisedMail ( $watchingUser , $mail , $article ) {
global $wgLang ;
$to = $watchingUser -> getName () . ' <' . $watchingUser -> getEmail () . '>' ;
$body = str_replace ( '$WATCHINGUSERNAME' , $watchingUser -> getName () , $mail -> body );
$body = str_replace ( '$WATCHINGUSEREMAILADDR' , $watchingUser -> getEmail (), $body );
$timecorrection = $watchingUser -> getOption ( 'timecorrection' );
if ( ! $timecorrection ) {
# fail safe - I prefer it. TomGries
$timecorrection = '00:00' ;
}
# $PAGEEDITDATE is the time and date of the page change
# expressed in terms of individual local time of the notification
# recipient, i.e. watching user
$body = str_replace ( '$PAGEEDITDATE' ,
2005-04-10 18:42:16 +00:00
$wgLang -> timeanddate ( $article -> mTimestamp , true , false , $timecorrection ),
2004-12-18 10:23:54 +00:00
$body );
$error = userMailer ( $to , $mail -> from , $mail -> subject , $body , $mail -> replyto );
return ( $error == '' );
}
2004-12-18 03:47:11 +00:00
} # end of class EmailNotification
2004-04-01 13:05:20 +00:00
?>