MediaWiki
master
|
00001 <?php 00032 class MailAddress { 00038 function __construct( $address, $name = null, $realName = null ) { 00039 if ( is_object( $address ) && $address instanceof User ) { 00040 $this->address = $address->getEmail(); 00041 $this->name = $address->getName(); 00042 $this->realName = $address->getRealName(); 00043 } else { 00044 $this->address = strval( $address ); 00045 $this->name = strval( $name ); 00046 $this->realName = strval( $realName ); 00047 } 00048 } 00049 00054 function toString() { 00055 # PHP's mail() implementation under Windows is somewhat shite, and 00056 # can't handle "Joe Bloggs <[email protected]>" format email addresses, 00057 # so don't bother generating them 00058 if ( $this->address ) { 00059 if ( $this->name != '' && !wfIsWindows() ) { 00060 global $wgEnotifUseRealName; 00061 $name = ( $wgEnotifUseRealName && $this->realName ) ? $this->realName : $this->name; 00062 $quoted = UserMailer::quotedPrintable( $name ); 00063 if ( strpos( $quoted, '.' ) !== false || strpos( $quoted, ',' ) !== false ) { 00064 $quoted = '"' . $quoted . '"'; 00065 } 00066 return "$quoted <{$this->address}>"; 00067 } else { 00068 return $this->address; 00069 } 00070 } else { 00071 return ""; 00072 } 00073 } 00074 00075 function __toString() { 00076 return $this->toString(); 00077 } 00078 } 00079 00080 00084 class UserMailer { 00085 static $mErrorString; 00086 00097 protected static function sendWithPear( $mailer, $dest, $headers, $body ) { 00098 $mailResult = $mailer->send( $dest, $headers, $body ); 00099 00100 # Based on the result return an error string, 00101 if ( PEAR::isError( $mailResult ) ) { 00102 wfDebug( "PEAR::Mail failed: " . $mailResult->getMessage() . "\n" ); 00103 return Status::newFatal( 'pear-mail-error', $mailResult->getMessage() ); 00104 } else { 00105 return Status::newGood(); 00106 } 00107 } 00108 00117 static function arrayToHeaderString( $headers, $endl = "\n" ) { 00118 $strings = array(); 00119 foreach( $headers as $name => $value ) { 00120 $strings[] = "$name: $value"; 00121 } 00122 return implode( $endl, $strings ); 00123 } 00124 00130 static function makeMsgId() { 00131 global $wgSMTP, $wgServer; 00132 00133 $msgid = uniqid( wfWikiID() . ".", true ); /* true required for cygwin */ 00134 if ( is_array($wgSMTP) && isset($wgSMTP['IDHost']) && $wgSMTP['IDHost'] ) { 00135 $domain = $wgSMTP['IDHost']; 00136 } else { 00137 $url = wfParseUrl($wgServer); 00138 $domain = $url['host']; 00139 } 00140 return "<$msgid@$domain>"; 00141 } 00142 00158 public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8' ) { 00159 global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams; 00160 00161 if ( !is_array( $to ) ) { 00162 $to = array( $to ); 00163 } 00164 00165 wfDebug( __METHOD__ . ': sending mail to ' . implode( ', ', $to ) . "\n" ); 00166 00167 # Make sure we have at least one address 00168 $has_address = false; 00169 foreach ( $to as $u ) { 00170 if ( $u->address ) { 00171 $has_address = true; 00172 break; 00173 } 00174 } 00175 if ( !$has_address ) { 00176 return Status::newFatal( 'user-mail-no-addy' ); 00177 } 00178 00179 # Forge email headers 00180 # ------------------- 00181 # 00182 # WARNING 00183 # 00184 # DO NOT add To: or Subject: headers at this step. They need to be 00185 # handled differently depending upon the mailer we are going to use. 00186 # 00187 # To: 00188 # PHP mail() first argument is the mail receiver. The argument is 00189 # used as a recipient destination and as a To header. 00190 # 00191 # PEAR mailer has a recipient argument which is only used to 00192 # send the mail. If no To header is given, PEAR will set it to 00193 # to 'undisclosed-recipients:'. 00194 # 00195 # NOTE: To: is for presentation, the actual recipient is specified 00196 # by the mailer using the Rcpt-To: header. 00197 # 00198 # Subject: 00199 # PHP mail() second argument to pass the subject, passing a Subject 00200 # as an additional header will result in a duplicate header. 00201 # 00202 # PEAR mailer should be passed a Subject header. 00203 # 00204 # -- hashar 20120218 00205 00206 $headers['From'] = $from->toString(); 00207 $headers['Return-Path'] = $from->address; 00208 00209 if ( $replyto ) { 00210 $headers['Reply-To'] = $replyto->toString(); 00211 } 00212 00213 $headers['Date'] = date( 'r' ); 00214 $headers['MIME-Version'] = '1.0'; 00215 $headers['Content-type'] = ( is_null( $contentType ) ? 00216 'text/plain; charset=UTF-8' : $contentType ); 00217 $headers['Content-transfer-encoding'] = '8bit'; 00218 00219 $headers['Message-ID'] = self::makeMsgId(); 00220 $headers['X-Mailer'] = 'MediaWiki mailer'; 00221 00222 $ret = wfRunHooks( 'AlternateUserMailer', array( $headers, $to, $from, $subject, $body ) ); 00223 if ( $ret === false ) { 00224 return Status::newGood(); 00225 } elseif ( $ret !== true ) { 00226 return Status::newFatal( 'php-mail-error', $ret ); 00227 } 00228 00229 if ( is_array( $wgSMTP ) ) { 00230 # 00231 # PEAR MAILER 00232 # 00233 00234 if ( function_exists( 'stream_resolve_include_path' ) ) { 00235 $found = stream_resolve_include_path( 'Mail.php' ); 00236 } else { 00237 $found = Fallback::stream_resolve_include_path( 'Mail.php' ); 00238 } 00239 if ( !$found ) { 00240 throw new MWException( 'PEAR mail package is not installed' ); 00241 } 00242 require_once( 'Mail.php' ); 00243 00244 wfSuppressWarnings(); 00245 00246 // Create the mail object using the Mail::factory method 00247 $mail_object =& Mail::factory( 'smtp', $wgSMTP ); 00248 if ( PEAR::isError( $mail_object ) ) { 00249 wfDebug( "PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n" ); 00250 wfRestoreWarnings(); 00251 return Status::newFatal( 'pear-mail-error', $mail_object->getMessage() ); 00252 } 00253 00254 wfDebug( "Sending mail via PEAR::Mail\n" ); 00255 00256 $headers['Subject'] = self::quotedPrintable( $subject ); 00257 00258 # When sending only to one recipient, shows it its email using To: 00259 if ( count( $to ) == 1 ) { 00260 $headers['To'] = $to[0]->toString(); 00261 } 00262 00263 # Split jobs since SMTP servers tends to limit the maximum 00264 # number of possible recipients. 00265 $chunks = array_chunk( $to, $wgEnotifMaxRecips ); 00266 foreach ( $chunks as $chunk ) { 00267 $status = self::sendWithPear( $mail_object, $chunk, $headers, $body ); 00268 # FIXME : some chunks might be sent while others are not! 00269 if ( !$status->isOK() ) { 00270 wfRestoreWarnings(); 00271 return $status; 00272 } 00273 } 00274 wfRestoreWarnings(); 00275 return Status::newGood(); 00276 } else { 00277 # 00278 # PHP mail() 00279 # 00280 00281 # Line endings need to be different on Unix and Windows due to 00282 # the bug described at http://trac.wordpress.org/ticket/2603 00283 if ( wfIsWindows() ) { 00284 $body = str_replace( "\n", "\r\n", $body ); 00285 $endl = "\r\n"; 00286 } else { 00287 $endl = "\n"; 00288 } 00289 00290 if( count($to) > 1 ) { 00291 $headers['To'] = 'undisclosed-recipients:;'; 00292 } 00293 $headers = self::arrayToHeaderString( $headers, $endl ); 00294 00295 wfDebug( "Sending mail via internal mail() function\n" ); 00296 00297 self::$mErrorString = ''; 00298 $html_errors = ini_get( 'html_errors' ); 00299 ini_set( 'html_errors', '0' ); 00300 set_error_handler( 'UserMailer::errorHandler' ); 00301 00302 $safeMode = wfIniGetBool( 'safe_mode' ); 00303 foreach ( $to as $recip ) { 00304 if ( $safeMode ) { 00305 $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers ); 00306 } else { 00307 $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers, $wgAdditionalMailParams ); 00308 } 00309 } 00310 00311 restore_error_handler(); 00312 ini_set( 'html_errors', $html_errors ); 00313 00314 if ( self::$mErrorString ) { 00315 wfDebug( "Error sending mail: " . self::$mErrorString . "\n" ); 00316 return Status::newFatal( 'php-mail-error', self::$mErrorString ); 00317 } elseif ( ! $sent ) { 00318 // mail function only tells if there's an error 00319 wfDebug( "Unknown error sending mail\n" ); 00320 return Status::newFatal( 'php-mail-error-unknown' ); 00321 } else { 00322 return Status::newGood(); 00323 } 00324 } 00325 } 00326 00333 static function errorHandler( $code, $string ) { 00334 self::$mErrorString = preg_replace( '/^mail\(\)(\s*\[.*?\])?: /', '', $string ); 00335 } 00336 00342 public static function rfc822Phrase( $phrase ) { 00343 $phrase = strtr( $phrase, array( "\r" => '', "\n" => '', '"' => '' ) ); 00344 return '"' . $phrase . '"'; 00345 } 00346 00352 public static function quotedPrintable( $string, $charset = '' ) { 00353 # Probably incomplete; see RFC 2045 00354 if( empty( $charset ) ) { 00355 $charset = 'UTF-8'; 00356 } 00357 $charset = strtoupper( $charset ); 00358 $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? 00359 00360 $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; 00361 $replace = $illegal . '\t ?_'; 00362 if( !preg_match( "/[$illegal]/", $string ) ) { 00363 return $string; 00364 } 00365 $out = "=?$charset?Q?"; 00366 $out .= preg_replace_callback( "/([$replace])/", 00367 array( __CLASS__, 'quotedPrintableCallback' ), $string ); 00368 $out .= '?='; 00369 return $out; 00370 } 00371 00372 protected static function quotedPrintableCallback( $matches ) { 00373 return sprintf( "=%02X", ord( $matches[1] ) ); 00374 } 00375 } 00376 00397 class EmailNotification { 00398 protected $subject, $body, $replyto, $from; 00399 protected $timestamp, $summary, $minorEdit, $oldid, $composed_common; 00400 protected $mailTargets = array(); 00401 00405 protected $title; 00406 00410 protected $editor; 00411 00425 public function notifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid = false ) { 00426 global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifMinorEdits, 00427 $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk; 00428 00429 if ( $title->getNamespace() < 0 ) { 00430 return; 00431 } 00432 00433 // Build a list of users to notfiy 00434 $watchers = array(); 00435 if ( $wgEnotifWatchlist || $wgShowUpdatedMarker ) { 00436 $dbw = wfGetDB( DB_MASTER ); 00437 $res = $dbw->select( array( 'watchlist' ), 00438 array( 'wl_user' ), 00439 array( 00440 'wl_user != ' . intval( $editor->getID() ), 00441 'wl_namespace' => $title->getNamespace(), 00442 'wl_title' => $title->getDBkey(), 00443 'wl_notificationtimestamp IS NULL', 00444 ), __METHOD__ 00445 ); 00446 foreach ( $res as $row ) { 00447 $watchers[] = intval( $row->wl_user ); 00448 } 00449 if ( $watchers ) { 00450 // Update wl_notificationtimestamp for all watching users except the editor 00451 $fname = __METHOD__; 00452 $dbw->onTransactionIdle( 00453 function() use ( $dbw, $timestamp, $watchers, $title, $fname ) { 00454 $dbw->begin( $fname ); 00455 $dbw->update( 'watchlist', 00456 array( /* SET */ 00457 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) 00458 ), array( /* WHERE */ 00459 'wl_user' => $watchers, 00460 'wl_namespace' => $title->getNamespace(), 00461 'wl_title' => $title->getDBkey(), 00462 ), $fname 00463 ); 00464 $dbw->commit( $fname ); 00465 } 00466 ); 00467 } 00468 } 00469 00470 $sendEmail = true; 00471 // If nobody is watching the page, and there are no users notified on all changes 00472 // don't bother creating a job/trying to send emails 00473 // $watchers deals with $wgEnotifWatchlist 00474 if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) { 00475 $sendEmail = false; 00476 // Only send notification for non minor edits, unless $wgEnotifMinorEdits 00477 if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { 00478 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00479 if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) { 00480 $sendEmail = true; 00481 } 00482 } 00483 } 00484 00485 if ( !$sendEmail ) { 00486 return; 00487 } 00488 if ( $wgEnotifUseJobQ ) { 00489 $params = array( 00490 'editor' => $editor->getName(), 00491 'editorID' => $editor->getID(), 00492 'timestamp' => $timestamp, 00493 'summary' => $summary, 00494 'minorEdit' => $minorEdit, 00495 'oldid' => $oldid, 00496 'watchers' => $watchers 00497 ); 00498 $job = new EnotifNotifyJob( $title, $params ); 00499 $job->insert(); 00500 } else { 00501 $this->actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers ); 00502 } 00503 } 00504 00519 public function actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers ) { 00520 # we use $wgPasswordSender as sender's address 00521 global $wgEnotifWatchlist; 00522 global $wgEnotifMinorEdits, $wgEnotifUserTalk; 00523 00524 wfProfileIn( __METHOD__ ); 00525 00526 # The following code is only run, if several conditions are met: 00527 # 1. EmailNotification for pages (other than user_talk pages) must be enabled 00528 # 2. minor edits (changes) are only regarded if the global flag indicates so 00529 00530 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00531 00532 $this->title = $title; 00533 $this->timestamp = $timestamp; 00534 $this->summary = $summary; 00535 $this->minorEdit = $minorEdit; 00536 $this->oldid = $oldid; 00537 $this->editor = $editor; 00538 $this->composed_common = false; 00539 00540 $userTalkId = false; 00541 00542 if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { 00543 00544 if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) { 00545 $targetUser = User::newFromName( $title->getText() ); 00546 $this->compose( $targetUser ); 00547 $userTalkId = $targetUser->getId(); 00548 } 00549 00550 if ( $wgEnotifWatchlist ) { 00551 // Send updates to watchers other than the current editor 00552 $userArray = UserArray::newFromIDs( $watchers ); 00553 foreach ( $userArray as $watchingUser ) { 00554 if ( $watchingUser->getOption( 'enotifwatchlistpages' ) && 00555 ( !$minorEdit || $watchingUser->getOption( 'enotifminoredits' ) ) && 00556 $watchingUser->isEmailConfirmed() && 00557 $watchingUser->getID() != $userTalkId ) 00558 { 00559 $this->compose( $watchingUser ); 00560 } 00561 } 00562 } 00563 } 00564 00565 global $wgUsersNotifiedOnAllChanges; 00566 foreach ( $wgUsersNotifiedOnAllChanges as $name ) { 00567 if ( $editor->getName() == $name ) { 00568 // No point notifying the user that actually made the change! 00569 continue; 00570 } 00571 $user = User::newFromName( $name ); 00572 $this->compose( $user ); 00573 } 00574 00575 $this->sendMails(); 00576 wfProfileOut( __METHOD__ ); 00577 } 00578 00585 private function canSendUserTalkEmail( $editor, $title, $minorEdit ) { 00586 global $wgEnotifUserTalk; 00587 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00588 00589 if ( $wgEnotifUserTalk && $isUserTalkPage ) { 00590 $targetUser = User::newFromName( $title->getText() ); 00591 00592 if ( !$targetUser || $targetUser->isAnon() ) { 00593 wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" ); 00594 } elseif ( $targetUser->getId() == $editor->getId() ) { 00595 wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" ); 00596 } elseif ( $targetUser->getOption( 'enotifusertalkpages' ) && 00597 ( !$minorEdit || $targetUser->getOption( 'enotifminoredits' ) ) ) 00598 { 00599 if ( $targetUser->isEmailConfirmed() ) { 00600 wfDebug( __METHOD__ . ": sending talk page update notification\n" ); 00601 return true; 00602 } else { 00603 wfDebug( __METHOD__ . ": talk page owner doesn't have validated email\n" ); 00604 } 00605 } else { 00606 wfDebug( __METHOD__ . ": talk page owner doesn't want notifications\n" ); 00607 } 00608 } 00609 return false; 00610 } 00611 00615 private function composeCommonMailtext() { 00616 global $wgPasswordSender, $wgPasswordSenderName, $wgNoReplyAddress; 00617 global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress; 00618 global $wgEnotifImpersonal, $wgEnotifUseRealName; 00619 00620 $this->composed_common = true; 00621 00622 # You as the WikiAdmin and Sysops can make use of plenty of 00623 # named variables when composing your notification emails while 00624 # simply editing the Meta pages 00625 00626 $keys = array(); 00627 $postTransformKeys = array(); 00628 00629 if ( $this->oldid ) { 00630 // Always show a link to the diff which triggered the mail. See bug 32210. 00631 $keys['$NEWPAGE'] = wfMessage( 'enotif_lastdiff', 00632 $this->title->getCanonicalUrl( 'diff=next&oldid=' . $this->oldid ) ) 00633 ->inContentLanguage()->text(); 00634 if ( !$wgEnotifImpersonal ) { 00635 // For personal mail, also show a link to the diff of all changes 00636 // since last visited. 00637 $keys['$NEWPAGE'] .= " \n" . wfMessage( 'enotif_lastvisited', 00638 $this->title->getCanonicalUrl( 'diff=0&oldid=' . $this->oldid ) ) 00639 ->inContentLanguage()->text(); 00640 } 00641 $keys['$OLDID'] = $this->oldid; 00642 $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text(); 00643 } else { 00644 $keys['$NEWPAGE'] = wfMessage( 'enotif_newpagetext' )->inContentLanguage()->text(); 00645 # clear $OLDID placeholder in the message template 00646 $keys['$OLDID'] = ''; 00647 $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text(); 00648 } 00649 00650 $keys['$PAGETITLE'] = $this->title->getPrefixedText(); 00651 $keys['$PAGETITLE_URL'] = $this->title->getCanonicalUrl(); 00652 $keys['$PAGEMINOREDIT'] = $this->minorEdit ? 00653 wfMessage( 'minoredit' )->inContentLanguage()->text() : ''; 00654 $keys['$UNWATCHURL'] = $this->title->getCanonicalUrl( 'action=unwatch' ); 00655 00656 if ( $this->editor->isAnon() ) { 00657 # real anon (user:xxx.xxx.xxx.xxx) 00658 $keys['$PAGEEDITOR'] = wfMessage( 'enotif_anon_editor', $this->editor->getName() ) 00659 ->inContentLanguage()->text(); 00660 $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' )->inContentLanguage()->text(); 00661 } else { 00662 $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? $this->editor->getRealName() : $this->editor->getName(); 00663 $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $this->editor->getName() ); 00664 $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalUrl(); 00665 } 00666 00667 $keys['$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalUrl(); 00668 00669 # Replace this after transforming the message, bug 35019 00670 $postTransformKeys['$PAGESUMMARY'] = $this->summary == '' ? ' - ' : $this->summary; 00671 00672 # Now build message's subject and body 00673 00674 $subject = wfMessage( 'enotif_subject' )->inContentLanguage()->plain(); 00675 $subject = strtr( $subject, $keys ); 00676 $subject = MessageCache::singleton()->transform( $subject, false, null, $this->title ); 00677 $this->subject = strtr( $subject, $postTransformKeys ); 00678 00679 $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain(); 00680 $body = strtr( $body, $keys ); 00681 $body = MessageCache::singleton()->transform( $body, false, null, $this->title ); 00682 $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 ); 00683 00684 # Reveal the page editor's address as REPLY-TO address only if 00685 # the user has not opted-out and the option is enabled at the 00686 # global configuration level. 00687 $adminAddress = new MailAddress( $wgPasswordSender, $wgPasswordSenderName ); 00688 if ( $wgEnotifRevealEditorAddress 00689 && ( $this->editor->getEmail() != '' ) 00690 && $this->editor->getOption( 'enotifrevealaddr' ) ) 00691 { 00692 $editorAddress = new MailAddress( $this->editor ); 00693 if ( $wgEnotifFromEditor ) { 00694 $this->from = $editorAddress; 00695 } else { 00696 $this->from = $adminAddress; 00697 $this->replyto = $editorAddress; 00698 } 00699 } else { 00700 $this->from = $adminAddress; 00701 $this->replyto = new MailAddress( $wgNoReplyAddress ); 00702 } 00703 } 00704 00712 function compose( $user ) { 00713 global $wgEnotifImpersonal; 00714 00715 if ( !$this->composed_common ) 00716 $this->composeCommonMailtext(); 00717 00718 if ( $wgEnotifImpersonal ) { 00719 $this->mailTargets[] = new MailAddress( $user ); 00720 } else { 00721 $this->sendPersonalised( $user ); 00722 } 00723 } 00724 00728 function sendMails() { 00729 global $wgEnotifImpersonal; 00730 if ( $wgEnotifImpersonal ) { 00731 $this->sendImpersonal( $this->mailTargets ); 00732 } 00733 } 00734 00744 function sendPersonalised( $watchingUser ) { 00745 global $wgContLang, $wgEnotifUseRealName; 00746 // From the PHP manual: 00747 // Note: The to parameter cannot be an address in the form of "Something <[email protected]>". 00748 // The mail command will not parse this properly while talking with the MTA. 00749 $to = new MailAddress( $watchingUser ); 00750 00751 # $PAGEEDITDATE is the time and date of the page change 00752 # expressed in terms of individual local time of the notification 00753 # recipient, i.e. watching user 00754 $body = str_replace( 00755 array( '$WATCHINGUSERNAME', 00756 '$PAGEEDITDATE', 00757 '$PAGEEDITTIME' ), 00758 array( $wgEnotifUseRealName ? $watchingUser->getRealName() : $watchingUser->getName(), 00759 $wgContLang->userDate( $this->timestamp, $watchingUser ), 00760 $wgContLang->userTime( $this->timestamp, $watchingUser ) ), 00761 $this->body ); 00762 00763 return UserMailer::send( $to, $this->from, $this->subject, $body, $this->replyto ); 00764 } 00765 00771 function sendImpersonal( $addresses ) { 00772 global $wgContLang; 00773 00774 if ( empty( $addresses ) ) 00775 return; 00776 00777 $body = str_replace( 00778 array( '$WATCHINGUSERNAME', 00779 '$PAGEEDITDATE', 00780 '$PAGEEDITTIME' ), 00781 array( wfMessage( 'enotif_impersonal_salutation' )->inContentLanguage()->text(), 00782 $wgContLang->date( $this->timestamp, false, false ), 00783 $wgContLang->time( $this->timestamp, false, false ) ), 00784 $this->body ); 00785 00786 return UserMailer::send( $addresses, $this->from, $this->subject, $body, $this->replyto ); 00787 } 00788 00789 } # end of class EmailNotification