MediaWiki
REL1_23
|
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 00083 class UserMailer { 00084 static $mErrorString; 00085 00096 protected static function sendWithPear( $mailer, $dest, $headers, $body ) { 00097 $mailResult = $mailer->send( $dest, $headers, $body ); 00098 00099 # Based on the result return an error string, 00100 if ( PEAR::isError( $mailResult ) ) { 00101 wfDebug( "PEAR::Mail failed: " . $mailResult->getMessage() . "\n" ); 00102 return Status::newFatal( 'pear-mail-error', $mailResult->getMessage() ); 00103 } else { 00104 return Status::newGood(); 00105 } 00106 } 00107 00120 static function arrayToHeaderString( $headers, $endl = "\n" ) { 00121 $strings = array(); 00122 foreach ( $headers as $name => $value ) { 00123 // Prevent header injection by stripping newlines from value 00124 $value = self::sanitizeHeaderValue( $value ); 00125 $strings[] = "$name: $value"; 00126 } 00127 return implode( $endl, $strings ); 00128 } 00129 00135 static function makeMsgId() { 00136 global $wgSMTP, $wgServer; 00137 00138 $msgid = uniqid( wfWikiID() . ".", true ); /* true required for cygwin */ 00139 if ( is_array( $wgSMTP ) && isset( $wgSMTP['IDHost'] ) && $wgSMTP['IDHost'] ) { 00140 $domain = $wgSMTP['IDHost']; 00141 } else { 00142 $url = wfParseUrl( $wgServer ); 00143 $domain = $url['host']; 00144 } 00145 return "<$msgid@$domain>"; 00146 } 00147 00163 public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8' ) { 00164 global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams, $wgAllowHTMLEmail; 00165 $mime = null; 00166 if ( !is_array( $to ) ) { 00167 $to = array( $to ); 00168 } 00169 00170 // mail body must have some content 00171 $minBodyLen = 10; 00172 // arbitrary but longer than Array or Object to detect casting error 00173 00174 // body must either be a string or an array with text and body 00175 if ( 00176 !( 00177 !is_array( $body ) && 00178 strlen( $body ) >= $minBodyLen 00179 ) 00180 && 00181 !( 00182 is_array( $body ) && 00183 isset( $body['text'] ) && 00184 isset( $body['html'] ) && 00185 strlen( $body['text'] ) >= $minBodyLen && 00186 strlen( $body['html'] ) >= $minBodyLen 00187 ) 00188 ) { 00189 // if it is neither we have a problem 00190 return Status::newFatal( 'user-mail-no-body' ); 00191 } 00192 00193 if ( !$wgAllowHTMLEmail && is_array( $body ) ) { 00194 // HTML not wanted. Dump it. 00195 $body = $body['text']; 00196 } 00197 00198 wfDebug( __METHOD__ . ': sending mail to ' . implode( ', ', $to ) . "\n" ); 00199 00200 # Make sure we have at least one address 00201 $has_address = false; 00202 foreach ( $to as $u ) { 00203 if ( $u->address ) { 00204 $has_address = true; 00205 break; 00206 } 00207 } 00208 if ( !$has_address ) { 00209 return Status::newFatal( 'user-mail-no-addy' ); 00210 } 00211 00212 # Forge email headers 00213 # ------------------- 00214 # 00215 # WARNING 00216 # 00217 # DO NOT add To: or Subject: headers at this step. They need to be 00218 # handled differently depending upon the mailer we are going to use. 00219 # 00220 # To: 00221 # PHP mail() first argument is the mail receiver. The argument is 00222 # used as a recipient destination and as a To header. 00223 # 00224 # PEAR mailer has a recipient argument which is only used to 00225 # send the mail. If no To header is given, PEAR will set it to 00226 # to 'undisclosed-recipients:'. 00227 # 00228 # NOTE: To: is for presentation, the actual recipient is specified 00229 # by the mailer using the Rcpt-To: header. 00230 # 00231 # Subject: 00232 # PHP mail() second argument to pass the subject, passing a Subject 00233 # as an additional header will result in a duplicate header. 00234 # 00235 # PEAR mailer should be passed a Subject header. 00236 # 00237 # -- hashar 20120218 00238 00239 $headers['From'] = $from->toString(); 00240 $headers['Return-Path'] = $from->address; 00241 00242 if ( $replyto ) { 00243 $headers['Reply-To'] = $replyto->toString(); 00244 } 00245 00246 $headers['Date'] = MWTimestamp::getLocalInstance()->format( 'r' ); 00247 $headers['Message-ID'] = self::makeMsgId(); 00248 $headers['X-Mailer'] = 'MediaWiki mailer'; 00249 00250 # Line endings need to be different on Unix and Windows due to 00251 # the bug described at http://trac.wordpress.org/ticket/2603 00252 if ( wfIsWindows() ) { 00253 $endl = "\r\n"; 00254 } else { 00255 $endl = "\n"; 00256 } 00257 00258 if ( is_array( $body ) ) { 00259 // we are sending a multipart message 00260 wfDebug( "Assembling multipart mime email\n" ); 00261 if ( !stream_resolve_include_path( 'Mail/mime.php' ) ) { 00262 wfDebug( "PEAR Mail_Mime package is not installed. Falling back to text email.\n" ); 00263 // remove the html body for text email fall back 00264 $body = $body['text']; 00265 } else { 00266 require_once 'Mail/mime.php'; 00267 if ( wfIsWindows() ) { 00268 $body['text'] = str_replace( "\n", "\r\n", $body['text'] ); 00269 $body['html'] = str_replace( "\n", "\r\n", $body['html'] ); 00270 } 00271 $mime = new Mail_mime( array( 'eol' => $endl, 'text_charset' => 'UTF-8', 'html_charset' => 'UTF-8' ) ); 00272 $mime->setTXTBody( $body['text'] ); 00273 $mime->setHTMLBody( $body['html'] ); 00274 $body = $mime->get(); // must call get() before headers() 00275 $headers = $mime->headers( $headers ); 00276 } 00277 } 00278 if ( $mime === null ) { 00279 // sending text only, either deliberately or as a fallback 00280 if ( wfIsWindows() ) { 00281 $body = str_replace( "\n", "\r\n", $body ); 00282 } 00283 $headers['MIME-Version'] = '1.0'; 00284 $headers['Content-type'] = ( is_null( $contentType ) ? 00285 'text/plain; charset=UTF-8' : $contentType ); 00286 $headers['Content-transfer-encoding'] = '8bit'; 00287 } 00288 00289 $ret = wfRunHooks( 'AlternateUserMailer', array( $headers, $to, $from, $subject, $body ) ); 00290 if ( $ret === false ) { 00291 // the hook implementation will return false to skip regular mail sending 00292 return Status::newGood(); 00293 } elseif ( $ret !== true ) { 00294 // the hook implementation will return a string to pass an error message 00295 return Status::newFatal( 'php-mail-error', $ret ); 00296 } 00297 00298 if ( is_array( $wgSMTP ) ) { 00299 # 00300 # PEAR MAILER 00301 # 00302 00303 if ( !stream_resolve_include_path( 'Mail.php' ) ) { 00304 throw new MWException( 'PEAR mail package is not installed' ); 00305 } 00306 require_once 'Mail.php'; 00307 00308 wfSuppressWarnings(); 00309 00310 // Create the mail object using the Mail::factory method 00311 $mail_object =& Mail::factory( 'smtp', $wgSMTP ); 00312 if ( PEAR::isError( $mail_object ) ) { 00313 wfDebug( "PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n" ); 00314 wfRestoreWarnings(); 00315 return Status::newFatal( 'pear-mail-error', $mail_object->getMessage() ); 00316 } 00317 00318 wfDebug( "Sending mail via PEAR::Mail\n" ); 00319 00320 $headers['Subject'] = self::quotedPrintable( $subject ); 00321 00322 # When sending only to one recipient, shows it its email using To: 00323 if ( count( $to ) == 1 ) { 00324 $headers['To'] = $to[0]->toString(); 00325 } 00326 00327 # Split jobs since SMTP servers tends to limit the maximum 00328 # number of possible recipients. 00329 $chunks = array_chunk( $to, $wgEnotifMaxRecips ); 00330 foreach ( $chunks as $chunk ) { 00331 $status = self::sendWithPear( $mail_object, $chunk, $headers, $body ); 00332 # FIXME : some chunks might be sent while others are not! 00333 if ( !$status->isOK() ) { 00334 wfRestoreWarnings(); 00335 return $status; 00336 } 00337 } 00338 wfRestoreWarnings(); 00339 return Status::newGood(); 00340 } else { 00341 # 00342 # PHP mail() 00343 # 00344 if ( count( $to ) > 1 ) { 00345 $headers['To'] = 'undisclosed-recipients:;'; 00346 } 00347 $headers = self::arrayToHeaderString( $headers, $endl ); 00348 00349 wfDebug( "Sending mail via internal mail() function\n" ); 00350 00351 self::$mErrorString = ''; 00352 $html_errors = ini_get( 'html_errors' ); 00353 ini_set( 'html_errors', '0' ); 00354 set_error_handler( 'UserMailer::errorHandler' ); 00355 00356 try { 00357 $safeMode = wfIniGetBool( 'safe_mode' ); 00358 00359 foreach ( $to as $recip ) { 00360 if ( $safeMode ) { 00361 $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers ); 00362 } else { 00363 $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers, $wgAdditionalMailParams ); 00364 } 00365 } 00366 } catch ( Exception $e ) { 00367 restore_error_handler(); 00368 throw $e; 00369 } 00370 00371 restore_error_handler(); 00372 ini_set( 'html_errors', $html_errors ); 00373 00374 if ( self::$mErrorString ) { 00375 wfDebug( "Error sending mail: " . self::$mErrorString . "\n" ); 00376 return Status::newFatal( 'php-mail-error', self::$mErrorString ); 00377 } elseif ( ! $sent ) { 00378 // mail function only tells if there's an error 00379 wfDebug( "Unknown error sending mail\n" ); 00380 return Status::newFatal( 'php-mail-error-unknown' ); 00381 } else { 00382 return Status::newGood(); 00383 } 00384 } 00385 } 00386 00393 static function errorHandler( $code, $string ) { 00394 self::$mErrorString = preg_replace( '/^mail\(\)(\s*\[.*?\])?: /', '', $string ); 00395 } 00396 00402 public static function sanitizeHeaderValue( $val ) { 00403 return strtr( $val, array( "\r" => '', "\n" => '' ) ); 00404 } 00405 00411 public static function rfc822Phrase( $phrase ) { 00412 // Remove line breaks 00413 $phrase = self::sanitizeHeaderValue( $phrase ); 00414 // Remove quotes 00415 $phrase = str_replace( '"', '', $phrase ); 00416 return '"' . $phrase . '"'; 00417 } 00418 00430 public static function quotedPrintable( $string, $charset = '' ) { 00431 # Probably incomplete; see RFC 2045 00432 if ( empty( $charset ) ) { 00433 $charset = 'UTF-8'; 00434 } 00435 $charset = strtoupper( $charset ); 00436 $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? 00437 00438 $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; 00439 $replace = $illegal . '\t ?_'; 00440 if ( !preg_match( "/[$illegal]/", $string ) ) { 00441 return $string; 00442 } 00443 $out = "=?$charset?Q?"; 00444 $out .= preg_replace_callback( "/([$replace])/", 00445 array( __CLASS__, 'quotedPrintableCallback' ), $string ); 00446 $out .= '?='; 00447 return $out; 00448 } 00449 00450 protected static function quotedPrintableCallback( $matches ) { 00451 return sprintf( "=%02X", ord( $matches[1] ) ); 00452 } 00453 } 00454 00475 class EmailNotification { 00476 protected $subject, $body, $replyto, $from; 00477 protected $timestamp, $summary, $minorEdit, $oldid, $composed_common, $pageStatus; 00478 protected $mailTargets = array(); 00479 00483 protected $title; 00484 00488 protected $editor; 00489 00504 public function notifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid = false, $pageStatus = 'changed' ) { 00505 global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifMinorEdits, 00506 $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk; 00507 00508 if ( $title->getNamespace() < 0 ) { 00509 return; 00510 } 00511 00512 // Build a list of users to notify 00513 $watchers = array(); 00514 if ( $wgEnotifWatchlist || $wgShowUpdatedMarker ) { 00515 $dbw = wfGetDB( DB_MASTER ); 00516 $res = $dbw->select( array( 'watchlist' ), 00517 array( 'wl_user' ), 00518 array( 00519 'wl_user != ' . intval( $editor->getID() ), 00520 'wl_namespace' => $title->getNamespace(), 00521 'wl_title' => $title->getDBkey(), 00522 'wl_notificationtimestamp IS NULL', 00523 ), __METHOD__ 00524 ); 00525 foreach ( $res as $row ) { 00526 $watchers[] = intval( $row->wl_user ); 00527 } 00528 if ( $watchers ) { 00529 // Update wl_notificationtimestamp for all watching users except the editor 00530 $fname = __METHOD__; 00531 $dbw->onTransactionIdle( 00532 function() use ( $dbw, $timestamp, $watchers, $title, $fname ) { 00533 $dbw->begin( $fname ); 00534 $dbw->update( 'watchlist', 00535 array( /* SET */ 00536 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) 00537 ), array( /* WHERE */ 00538 'wl_user' => $watchers, 00539 'wl_namespace' => $title->getNamespace(), 00540 'wl_title' => $title->getDBkey(), 00541 ), $fname 00542 ); 00543 $dbw->commit( $fname ); 00544 } 00545 ); 00546 } 00547 } 00548 00549 $sendEmail = true; 00550 // If nobody is watching the page, and there are no users notified on all changes 00551 // don't bother creating a job/trying to send emails 00552 // $watchers deals with $wgEnotifWatchlist 00553 if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) { 00554 $sendEmail = false; 00555 // Only send notification for non minor edits, unless $wgEnotifMinorEdits 00556 if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { 00557 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00558 if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) { 00559 $sendEmail = true; 00560 } 00561 } 00562 } 00563 00564 if ( !$sendEmail ) { 00565 return; 00566 } 00567 if ( $wgEnotifUseJobQ ) { 00568 $params = array( 00569 'editor' => $editor->getName(), 00570 'editorID' => $editor->getID(), 00571 'timestamp' => $timestamp, 00572 'summary' => $summary, 00573 'minorEdit' => $minorEdit, 00574 'oldid' => $oldid, 00575 'watchers' => $watchers, 00576 'pageStatus' => $pageStatus 00577 ); 00578 $job = new EnotifNotifyJob( $title, $params ); 00579 JobQueueGroup::singleton()->push( $job ); 00580 } else { 00581 $this->actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers, $pageStatus ); 00582 } 00583 } 00584 00601 public function actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, 00602 $oldid, $watchers, $pageStatus = 'changed' ) { 00603 # we use $wgPasswordSender as sender's address 00604 global $wgEnotifWatchlist; 00605 global $wgEnotifMinorEdits, $wgEnotifUserTalk; 00606 00607 wfProfileIn( __METHOD__ ); 00608 00609 # The following code is only run, if several conditions are met: 00610 # 1. EmailNotification for pages (other than user_talk pages) must be enabled 00611 # 2. minor edits (changes) are only regarded if the global flag indicates so 00612 00613 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00614 00615 $this->title = $title; 00616 $this->timestamp = $timestamp; 00617 $this->summary = $summary; 00618 $this->minorEdit = $minorEdit; 00619 $this->oldid = $oldid; 00620 $this->editor = $editor; 00621 $this->composed_common = false; 00622 $this->pageStatus = $pageStatus; 00623 00624 $formattedPageStatus = array( 'deleted', 'created', 'moved', 'restored', 'changed' ); 00625 00626 wfRunHooks( 'UpdateUserMailerFormattedPageStatus', array( &$formattedPageStatus ) ); 00627 if ( !in_array( $this->pageStatus, $formattedPageStatus ) ) { 00628 wfProfileOut( __METHOD__ ); 00629 throw new MWException( 'Not a valid page status!' ); 00630 } 00631 00632 $userTalkId = false; 00633 00634 if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { 00635 00636 if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) { 00637 $targetUser = User::newFromName( $title->getText() ); 00638 $this->compose( $targetUser ); 00639 $userTalkId = $targetUser->getId(); 00640 } 00641 00642 if ( $wgEnotifWatchlist ) { 00643 // Send updates to watchers other than the current editor 00644 $userArray = UserArray::newFromIDs( $watchers ); 00645 foreach ( $userArray as $watchingUser ) { 00646 if ( $watchingUser->getOption( 'enotifwatchlistpages' ) 00647 && ( !$minorEdit || $watchingUser->getOption( 'enotifminoredits' ) ) 00648 && $watchingUser->isEmailConfirmed() 00649 && $watchingUser->getID() != $userTalkId 00650 ) { 00651 if ( wfRunHooks( 'SendWatchlistEmailNotification', array( $watchingUser, $title, $this ) ) ) { 00652 $this->compose( $watchingUser ); 00653 } 00654 } 00655 } 00656 } 00657 } 00658 00659 global $wgUsersNotifiedOnAllChanges; 00660 foreach ( $wgUsersNotifiedOnAllChanges as $name ) { 00661 if ( $editor->getName() == $name ) { 00662 // No point notifying the user that actually made the change! 00663 continue; 00664 } 00665 $user = User::newFromName( $name ); 00666 $this->compose( $user ); 00667 } 00668 00669 $this->sendMails(); 00670 wfProfileOut( __METHOD__ ); 00671 } 00672 00679 private function canSendUserTalkEmail( $editor, $title, $minorEdit ) { 00680 global $wgEnotifUserTalk; 00681 $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); 00682 00683 if ( $wgEnotifUserTalk && $isUserTalkPage ) { 00684 $targetUser = User::newFromName( $title->getText() ); 00685 00686 if ( !$targetUser || $targetUser->isAnon() ) { 00687 wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" ); 00688 } elseif ( $targetUser->getId() == $editor->getId() ) { 00689 wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" ); 00690 } elseif ( $targetUser->getOption( 'enotifusertalkpages' ) 00691 && ( !$minorEdit || $targetUser->getOption( 'enotifminoredits' ) ) 00692 ) { 00693 if ( !$targetUser->isEmailConfirmed() ) { 00694 wfDebug( __METHOD__ . ": talk page owner doesn't have validated email\n" ); 00695 } elseif ( !wfRunHooks( 'AbortTalkPageEmailNotification', array( $targetUser, $title ) ) ) { 00696 wfDebug( __METHOD__ . ": talk page update notification is aborted for this user\n" ); 00697 } else { 00698 wfDebug( __METHOD__ . ": sending talk page update notification\n" ); 00699 return true; 00700 } 00701 } else { 00702 wfDebug( __METHOD__ . ": talk page owner doesn't want notifications\n" ); 00703 } 00704 } 00705 return false; 00706 } 00707 00711 private function composeCommonMailtext() { 00712 global $wgPasswordSender, $wgNoReplyAddress; 00713 global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress; 00714 global $wgEnotifImpersonal, $wgEnotifUseRealName; 00715 00716 $this->composed_common = true; 00717 00718 # You as the WikiAdmin and Sysops can make use of plenty of 00719 # named variables when composing your notification emails while 00720 # simply editing the Meta pages 00721 00722 $keys = array(); 00723 $postTransformKeys = array(); 00724 $pageTitleUrl = $this->title->getCanonicalURL(); 00725 $pageTitle = $this->title->getPrefixedText(); 00726 00727 if ( $this->oldid ) { 00728 // Always show a link to the diff which triggered the mail. See bug 32210. 00729 $keys['$NEWPAGE'] = "\n\n" . wfMessage( 'enotif_lastdiff', 00730 $this->title->getCanonicalURL( array( 'diff' => 'next', 'oldid' => $this->oldid ) ) ) 00731 ->inContentLanguage()->text(); 00732 00733 if ( !$wgEnotifImpersonal ) { 00734 // For personal mail, also show a link to the diff of all changes 00735 // since last visited. 00736 $keys['$NEWPAGE'] .= "\n\n" . wfMessage( 'enotif_lastvisited', 00737 $this->title->getCanonicalURL( array( 'diff' => '0', 'oldid' => $this->oldid ) ) ) 00738 ->inContentLanguage()->text(); 00739 } 00740 $keys['$OLDID'] = $this->oldid; 00741 // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. 00742 $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text(); 00743 } else { 00744 # clear $OLDID placeholder in the message template 00745 $keys['$OLDID'] = ''; 00746 $keys['$NEWPAGE'] = ''; 00747 // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. 00748 $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text(); 00749 } 00750 00751 $keys['$PAGETITLE'] = $this->title->getPrefixedText(); 00752 $keys['$PAGETITLE_URL'] = $this->title->getCanonicalURL(); 00753 $keys['$PAGEMINOREDIT'] = $this->minorEdit ? 00754 wfMessage( 'minoredit' )->inContentLanguage()->text() : ''; 00755 $keys['$UNWATCHURL'] = $this->title->getCanonicalURL( 'action=unwatch' ); 00756 00757 if ( $this->editor->isAnon() ) { 00758 # real anon (user:xxx.xxx.xxx.xxx) 00759 $keys['$PAGEEDITOR'] = wfMessage( 'enotif_anon_editor', $this->editor->getName() ) 00760 ->inContentLanguage()->text(); 00761 $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' )->inContentLanguage()->text(); 00762 00763 } else { 00764 $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? $this->editor->getRealName() : $this->editor->getName(); 00765 $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $this->editor->getName() ); 00766 $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalURL(); 00767 } 00768 00769 $keys['$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalURL(); 00770 $keys['$HELPPAGE'] = wfExpandUrl( Skin::makeInternalOrExternalUrl( wfMessage( 'helppage' )->inContentLanguage()->text() ) ); 00771 00772 # Replace this after transforming the message, bug 35019 00773 $postTransformKeys['$PAGESUMMARY'] = $this->summary == '' ? ' - ' : $this->summary; 00774 00775 // Now build message's subject and body 00776 00777 // Messages: 00778 // enotif_subject_deleted, enotif_subject_created, enotif_subject_moved, 00779 // enotif_subject_restored, enotif_subject_changed 00780 $this->subject = wfMessage( 'enotif_subject_' . $this->pageStatus )->inContentLanguage() 00781 ->params( $pageTitle, $keys['$PAGEEDITOR'] )->text(); 00782 00783 // Messages: 00784 // enotif_body_intro_deleted, enotif_body_intro_created, enotif_body_intro_moved, 00785 // enotif_body_intro_restored, enotif_body_intro_changed 00786 $keys['$PAGEINTRO'] = wfMessage( 'enotif_body_intro_' . $this->pageStatus ) 00787 ->inContentLanguage()->params( $pageTitle, $keys['$PAGEEDITOR'], $pageTitleUrl ) 00788 ->text(); 00789 00790 $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain(); 00791 $body = strtr( $body, $keys ); 00792 $body = MessageCache::singleton()->transform( $body, false, null, $this->title ); 00793 $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 ); 00794 00795 # Reveal the page editor's address as REPLY-TO address only if 00796 # the user has not opted-out and the option is enabled at the 00797 # global configuration level. 00798 $adminAddress = new MailAddress( $wgPasswordSender, 00799 wfMessage( 'emailsender' )->inContentLanguage()->text() ); 00800 if ( $wgEnotifRevealEditorAddress 00801 && ( $this->editor->getEmail() != '' ) 00802 && $this->editor->getOption( 'enotifrevealaddr' ) 00803 ) { 00804 $editorAddress = new MailAddress( $this->editor ); 00805 if ( $wgEnotifFromEditor ) { 00806 $this->from = $editorAddress; 00807 } else { 00808 $this->from = $adminAddress; 00809 $this->replyto = $editorAddress; 00810 } 00811 } else { 00812 $this->from = $adminAddress; 00813 $this->replyto = new MailAddress( $wgNoReplyAddress ); 00814 } 00815 } 00816 00824 function compose( $user ) { 00825 global $wgEnotifImpersonal; 00826 00827 if ( !$this->composed_common ) { 00828 $this->composeCommonMailtext(); 00829 } 00830 00831 if ( $wgEnotifImpersonal ) { 00832 $this->mailTargets[] = new MailAddress( $user ); 00833 } else { 00834 $this->sendPersonalised( $user ); 00835 } 00836 } 00837 00841 function sendMails() { 00842 global $wgEnotifImpersonal; 00843 if ( $wgEnotifImpersonal ) { 00844 $this->sendImpersonal( $this->mailTargets ); 00845 } 00846 } 00847 00857 function sendPersonalised( $watchingUser ) { 00858 global $wgContLang, $wgEnotifUseRealName; 00859 // From the PHP manual: 00860 // Note: The to parameter cannot be an address in the form of "Something <[email protected]>". 00861 // The mail command will not parse this properly while talking with the MTA. 00862 $to = new MailAddress( $watchingUser ); 00863 00864 # $PAGEEDITDATE is the time and date of the page change 00865 # expressed in terms of individual local time of the notification 00866 # recipient, i.e. watching user 00867 $body = str_replace( 00868 array( '$WATCHINGUSERNAME', 00869 '$PAGEEDITDATE', 00870 '$PAGEEDITTIME' ), 00871 array( $wgEnotifUseRealName ? $watchingUser->getRealName() : $watchingUser->getName(), 00872 $wgContLang->userDate( $this->timestamp, $watchingUser ), 00873 $wgContLang->userTime( $this->timestamp, $watchingUser ) ), 00874 $this->body ); 00875 00876 return UserMailer::send( $to, $this->from, $this->subject, $body, $this->replyto ); 00877 } 00878 00885 function sendImpersonal( $addresses ) { 00886 global $wgContLang; 00887 00888 if ( empty( $addresses ) ) { 00889 return null; 00890 } 00891 00892 $body = str_replace( 00893 array( '$WATCHINGUSERNAME', 00894 '$PAGEEDITDATE', 00895 '$PAGEEDITTIME' ), 00896 array( wfMessage( 'enotif_impersonal_salutation' )->inContentLanguage()->text(), 00897 $wgContLang->date( $this->timestamp, false, false ), 00898 $wgContLang->time( $this->timestamp, false, false ) ), 00899 $this->body ); 00900 00901 return UserMailer::send( $addresses, $this->from, $this->subject, $body, $this->replyto ); 00902 } 00903 00904 } # end of class EmailNotification