MediaWiki  1.23.12
UserMailer.php
Go to the documentation of this file.
1 <?php
32 class MailAddress {
38  function __construct( $address, $name = null, $realName = null ) {
39  if ( is_object( $address ) && $address instanceof User ) {
40  $this->address = $address->getEmail();
41  $this->name = $address->getName();
42  $this->realName = $address->getRealName();
43  } else {
44  $this->address = strval( $address );
45  $this->name = strval( $name );
46  $this->realName = strval( $realName );
47  }
48  }
49 
54  function toString() {
55  # PHP's mail() implementation under Windows is somewhat shite, and
56  # can't handle "Joe Bloggs <joe@bloggs.com>" format email addresses,
57  # so don't bother generating them
58  if ( $this->address ) {
59  if ( $this->name != '' && !wfIsWindows() ) {
60  global $wgEnotifUseRealName;
61  $name = ( $wgEnotifUseRealName && $this->realName ) ? $this->realName : $this->name;
63  if ( strpos( $quoted, '.' ) !== false || strpos( $quoted, ',' ) !== false ) {
64  $quoted = '"' . $quoted . '"';
65  }
66  return "$quoted <{$this->address}>";
67  } else {
68  return $this->address;
69  }
70  } else {
71  return "";
72  }
73  }
74 
75  function __toString() {
76  return $this->toString();
77  }
78 }
79 
83 class UserMailer {
84  static $mErrorString;
85 
96  protected static function sendWithPear( $mailer, $dest, $headers, $body ) {
97  $mailResult = $mailer->send( $dest, $headers, $body );
98 
99  # Based on the result return an error string,
100  if ( PEAR::isError( $mailResult ) ) {
101  wfDebug( "PEAR::Mail failed: " . $mailResult->getMessage() . "\n" );
102  return Status::newFatal( 'pear-mail-error', $mailResult->getMessage() );
103  } else {
104  return Status::newGood();
105  }
106  }
107 
120  static function arrayToHeaderString( $headers, $endl = "\n" ) {
121  $strings = array();
122  foreach ( $headers as $name => $value ) {
123  // Prevent header injection by stripping newlines from value
125  $strings[] = "$name: $value";
126  }
127  return implode( $endl, $strings );
128  }
129 
135  static function makeMsgId() {
136  global $wgSMTP, $wgServer;
137 
138  $msgid = uniqid( wfWikiID() . ".", true ); /* true required for cygwin */
139  if ( is_array( $wgSMTP ) && isset( $wgSMTP['IDHost'] ) && $wgSMTP['IDHost'] ) {
140  $domain = $wgSMTP['IDHost'];
141  } else {
142  $url = wfParseUrl( $wgServer );
143  $domain = $url['host'];
144  }
145  return "<$msgid@$domain>";
146  }
147 
163  public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8' ) {
164  global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams, $wgAllowHTMLEmail;
165  $mime = null;
166  if ( !is_array( $to ) ) {
167  $to = array( $to );
168  }
169 
170  // mail body must have some content
171  $minBodyLen = 10;
172  // arbitrary but longer than Array or Object to detect casting error
173 
174  // body must either be a string or an array with text and body
175  if (
176  !(
177  !is_array( $body ) &&
178  strlen( $body ) >= $minBodyLen
179  )
180  &&
181  !(
182  is_array( $body ) &&
183  isset( $body['text'] ) &&
184  isset( $body['html'] ) &&
185  strlen( $body['text'] ) >= $minBodyLen &&
186  strlen( $body['html'] ) >= $minBodyLen
187  )
188  ) {
189  // if it is neither we have a problem
190  return Status::newFatal( 'user-mail-no-body' );
191  }
192 
193  if ( !$wgAllowHTMLEmail && is_array( $body ) ) {
194  // HTML not wanted. Dump it.
195  $body = $body['text'];
196  }
197 
198  wfDebug( __METHOD__ . ': sending mail to ' . implode( ', ', $to ) . "\n" );
199 
200  # Make sure we have at least one address
201  $has_address = false;
202  foreach ( $to as $u ) {
203  if ( $u->address ) {
204  $has_address = true;
205  break;
206  }
207  }
208  if ( !$has_address ) {
209  return Status::newFatal( 'user-mail-no-addy' );
210  }
211 
212  # Forge email headers
213  # -------------------
214  #
215  # WARNING
216  #
217  # DO NOT add To: or Subject: headers at this step. They need to be
218  # handled differently depending upon the mailer we are going to use.
219  #
220  # To:
221  # PHP mail() first argument is the mail receiver. The argument is
222  # used as a recipient destination and as a To header.
223  #
224  # PEAR mailer has a recipient argument which is only used to
225  # send the mail. If no To header is given, PEAR will set it to
226  # to 'undisclosed-recipients:'.
227  #
228  # NOTE: To: is for presentation, the actual recipient is specified
229  # by the mailer using the Rcpt-To: header.
230  #
231  # Subject:
232  # PHP mail() second argument to pass the subject, passing a Subject
233  # as an additional header will result in a duplicate header.
234  #
235  # PEAR mailer should be passed a Subject header.
236  #
237  # -- hashar 20120218
238 
239  $headers['From'] = $from->toString();
240  $headers['Return-Path'] = $from->address;
241 
242  if ( $replyto ) {
243  $headers['Reply-To'] = $replyto->toString();
244  }
245 
246  $headers['Date'] = MWTimestamp::getLocalInstance()->format( 'r' );
247  $headers['Message-ID'] = self::makeMsgId();
248  $headers['X-Mailer'] = 'MediaWiki mailer';
249 
250  # Line endings need to be different on Unix and Windows due to
251  # the bug described at http://trac.wordpress.org/ticket/2603
252  if ( wfIsWindows() ) {
253  $endl = "\r\n";
254  } else {
255  $endl = "\n";
256  }
257 
258  if ( is_array( $body ) ) {
259  // we are sending a multipart message
260  wfDebug( "Assembling multipart mime email\n" );
261  if ( !stream_resolve_include_path( 'Mail/mime.php' ) ) {
262  wfDebug( "PEAR Mail_Mime package is not installed. Falling back to text email.\n" );
263  // remove the html body for text email fall back
264  $body = $body['text'];
265  } else {
266  require_once 'Mail/mime.php';
267  if ( wfIsWindows() ) {
268  $body['text'] = str_replace( "\n", "\r\n", $body['text'] );
269  $body['html'] = str_replace( "\n", "\r\n", $body['html'] );
270  }
271  $mime = new Mail_mime( array( 'eol' => $endl, 'text_charset' => 'UTF-8', 'html_charset' => 'UTF-8' ) );
272  $mime->setTXTBody( $body['text'] );
273  $mime->setHTMLBody( $body['html'] );
274  $body = $mime->get(); // must call get() before headers()
275  $headers = $mime->headers( $headers );
276  }
277  }
278  if ( $mime === null ) {
279  // sending text only, either deliberately or as a fallback
280  if ( wfIsWindows() ) {
281  $body = str_replace( "\n", "\r\n", $body );
282  }
283  $headers['MIME-Version'] = '1.0';
284  $headers['Content-type'] = ( is_null( $contentType ) ?
285  'text/plain; charset=UTF-8' : $contentType );
286  $headers['Content-transfer-encoding'] = '8bit';
287  }
288 
289  $ret = wfRunHooks( 'AlternateUserMailer', array( $headers, $to, $from, $subject, $body ) );
290  if ( $ret === false ) {
291  // the hook implementation will return false to skip regular mail sending
292  return Status::newGood();
293  } elseif ( $ret !== true ) {
294  // the hook implementation will return a string to pass an error message
295  return Status::newFatal( 'php-mail-error', $ret );
296  }
297 
298  if ( is_array( $wgSMTP ) ) {
299  #
300  # PEAR MAILER
301  #
302 
303  if ( !stream_resolve_include_path( 'Mail.php' ) ) {
304  throw new MWException( 'PEAR mail package is not installed' );
305  }
306  require_once 'Mail.php';
307 
309 
310  // Create the mail object using the Mail::factory method
311  $mail_object =& Mail::factory( 'smtp', $wgSMTP );
312  if ( PEAR::isError( $mail_object ) ) {
313  wfDebug( "PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n" );
315  return Status::newFatal( 'pear-mail-error', $mail_object->getMessage() );
316  }
317 
318  wfDebug( "Sending mail via PEAR::Mail\n" );
319 
320  $headers['Subject'] = self::quotedPrintable( $subject );
321 
322  # When sending only to one recipient, shows it its email using To:
323  if ( count( $to ) == 1 ) {
324  $headers['To'] = $to[0]->toString();
325  }
326 
327  # Split jobs since SMTP servers tends to limit the maximum
328  # number of possible recipients.
329  $chunks = array_chunk( $to, $wgEnotifMaxRecips );
330  foreach ( $chunks as $chunk ) {
331  $status = self::sendWithPear( $mail_object, $chunk, $headers, $body );
332  # FIXME : some chunks might be sent while others are not!
333  if ( !$status->isOK() ) {
335  return $status;
336  }
337  }
339  return Status::newGood();
340  } else {
341  #
342  # PHP mail()
343  #
344  if ( count( $to ) > 1 ) {
345  $headers['To'] = 'undisclosed-recipients:;';
346  }
347  $headers = self::arrayToHeaderString( $headers, $endl );
348 
349  wfDebug( "Sending mail via internal mail() function\n" );
350 
351  self::$mErrorString = '';
352  $html_errors = ini_get( 'html_errors' );
353  ini_set( 'html_errors', '0' );
354  set_error_handler( 'UserMailer::errorHandler' );
355 
356  try {
357  $safeMode = wfIniGetBool( 'safe_mode' );
358 
359  foreach ( $to as $recip ) {
360  if ( $safeMode ) {
361  $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers );
362  } else {
363  $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers, $wgAdditionalMailParams );
364  }
365  }
366  } catch ( Exception $e ) {
367  restore_error_handler();
368  throw $e;
369  }
370 
371  restore_error_handler();
372  ini_set( 'html_errors', $html_errors );
373 
374  if ( self::$mErrorString ) {
375  wfDebug( "Error sending mail: " . self::$mErrorString . "\n" );
376  return Status::newFatal( 'php-mail-error', self::$mErrorString );
377  } elseif ( ! $sent ) {
378  // mail function only tells if there's an error
379  wfDebug( "Unknown error sending mail\n" );
380  return Status::newFatal( 'php-mail-error-unknown' );
381  } else {
382  return Status::newGood();
383  }
384  }
385  }
386 
393  static function errorHandler( $code, $string ) {
394  self::$mErrorString = preg_replace( '/^mail\(\)(\s*\[.*?\])?: /', '', $string );
395  }
396 
402  public static function sanitizeHeaderValue( $val ) {
403  return strtr( $val, array( "\r" => '', "\n" => '' ) );
404  }
405 
411  public static function rfc822Phrase( $phrase ) {
412  // Remove line breaks
413  $phrase = self::sanitizeHeaderValue( $phrase );
414  // Remove quotes
415  $phrase = str_replace( '"', '', $phrase );
416  return '"' . $phrase . '"';
417  }
418 
430  public static function quotedPrintable( $string, $charset = '' ) {
431  # Probably incomplete; see RFC 2045
432  if ( empty( $charset ) ) {
433  $charset = 'UTF-8';
434  }
435  $charset = strtoupper( $charset );
436  $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ?
437 
438  $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff=';
439  $replace = $illegal . '\t ?_';
440  if ( !preg_match( "/[$illegal]/", $string ) ) {
441  return $string;
442  }
443  $out = "=?$charset?Q?";
444  $out .= preg_replace_callback( "/([$replace])/",
445  array( __CLASS__, 'quotedPrintableCallback' ), $string );
446  $out .= '?=';
447  return $out;
448  }
449 
450  protected static function quotedPrintableCallback( $matches ) {
451  return sprintf( "=%02X", ord( $matches[1] ) );
452  }
453 }
454 
476  protected $subject, $body, $replyto, $from;
478  protected $mailTargets = array();
479 
483  protected $title;
484 
488  protected $editor;
489 
504  public function notifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid = false, $pageStatus = 'changed' ) {
505  global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifMinorEdits,
506  $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk;
507 
508  if ( $title->getNamespace() < 0 ) {
509  return;
510  }
511 
512  // Build a list of users to notify
513  $watchers = array();
514  if ( $wgEnotifWatchlist || $wgShowUpdatedMarker ) {
515  $dbw = wfGetDB( DB_MASTER );
516  $res = $dbw->select( array( 'watchlist' ),
517  array( 'wl_user' ),
518  array(
519  'wl_user != ' . intval( $editor->getID() ),
520  'wl_namespace' => $title->getNamespace(),
521  'wl_title' => $title->getDBkey(),
522  'wl_notificationtimestamp IS NULL',
523  ), __METHOD__
524  );
525  foreach ( $res as $row ) {
526  $watchers[] = intval( $row->wl_user );
527  }
528  if ( $watchers ) {
529  // Update wl_notificationtimestamp for all watching users except the editor
530  $fname = __METHOD__;
531  $dbw->onTransactionIdle(
532  function() use ( $dbw, $timestamp, $watchers, $title, $fname ) {
533  $dbw->begin( $fname );
534  $dbw->update( 'watchlist',
535  array( /* SET */
536  'wl_notificationtimestamp' => $dbw->timestamp( $timestamp )
537  ), array( /* WHERE */
538  'wl_user' => $watchers,
539  'wl_namespace' => $title->getNamespace(),
540  'wl_title' => $title->getDBkey(),
541  ), $fname
542  );
543  $dbw->commit( $fname );
544  }
545  );
546  }
547  }
548 
549  $sendEmail = true;
550  // If nobody is watching the page, and there are no users notified on all changes
551  // don't bother creating a job/trying to send emails
552  // $watchers deals with $wgEnotifWatchlist
553  if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) {
554  $sendEmail = false;
555  // Only send notification for non minor edits, unless $wgEnotifMinorEdits
556  if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) {
557  $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK );
558  if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) {
559  $sendEmail = true;
560  }
561  }
562  }
563 
564  if ( !$sendEmail ) {
565  return;
566  }
567  if ( $wgEnotifUseJobQ ) {
568  $params = array(
569  'editor' => $editor->getName(),
570  'editorID' => $editor->getID(),
571  'timestamp' => $timestamp,
572  'summary' => $summary,
573  'minorEdit' => $minorEdit,
574  'oldid' => $oldid,
575  'watchers' => $watchers,
576  'pageStatus' => $pageStatus
577  );
579  JobQueueGroup::singleton()->push( $job );
580  } else {
582  }
583  }
584 
602  $oldid, $watchers, $pageStatus = 'changed' ) {
603  # we use $wgPasswordSender as sender's address
604  global $wgEnotifWatchlist;
605  global $wgEnotifMinorEdits, $wgEnotifUserTalk;
606 
607  wfProfileIn( __METHOD__ );
608 
609  # The following code is only run, if several conditions are met:
610  # 1. EmailNotification for pages (other than user_talk pages) must be enabled
611  # 2. minor edits (changes) are only regarded if the global flag indicates so
612 
613  $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK );
614 
615  $this->title = $title;
616  $this->timestamp = $timestamp;
617  $this->summary = $summary;
618  $this->minorEdit = $minorEdit;
619  $this->oldid = $oldid;
620  $this->editor = $editor;
621  $this->composed_common = false;
622  $this->pageStatus = $pageStatus;
623 
624  $formattedPageStatus = array( 'deleted', 'created', 'moved', 'restored', 'changed' );
625 
626  wfRunHooks( 'UpdateUserMailerFormattedPageStatus', array( &$formattedPageStatus ) );
627  if ( !in_array( $this->pageStatus, $formattedPageStatus ) ) {
628  wfProfileOut( __METHOD__ );
629  throw new MWException( 'Not a valid page status!' );
630  }
631 
632  $userTalkId = false;
633 
634  if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) {
635 
636  if ( $wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) ) {
637  $targetUser = User::newFromName( $title->getText() );
638  $this->compose( $targetUser );
639  $userTalkId = $targetUser->getId();
640  }
641 
642  if ( $wgEnotifWatchlist ) {
643  // Send updates to watchers other than the current editor
644  $userArray = UserArray::newFromIDs( $watchers );
645  foreach ( $userArray as $watchingUser ) {
646  if ( $watchingUser->getOption( 'enotifwatchlistpages' )
647  && ( !$minorEdit || $watchingUser->getOption( 'enotifminoredits' ) )
648  && $watchingUser->isEmailConfirmed()
649  && $watchingUser->getID() != $userTalkId
650  ) {
651  if ( wfRunHooks( 'SendWatchlistEmailNotification', array( $watchingUser, $title, $this ) ) ) {
652  $this->compose( $watchingUser );
653  }
654  }
655  }
656  }
657  }
658 
659  global $wgUsersNotifiedOnAllChanges;
660  foreach ( $wgUsersNotifiedOnAllChanges as $name ) {
661  if ( $editor->getName() == $name ) {
662  // No point notifying the user that actually made the change!
663  continue;
664  }
666  $this->compose( $user );
667  }
668 
669  $this->sendMails();
670  wfProfileOut( __METHOD__ );
671  }
672 
679  private function canSendUserTalkEmail( $editor, $title, $minorEdit ) {
680  global $wgEnotifUserTalk;
681  $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK );
682 
683  if ( $wgEnotifUserTalk && $isUserTalkPage ) {
684  $targetUser = User::newFromName( $title->getText() );
685 
686  if ( !$targetUser || $targetUser->isAnon() ) {
687  wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" );
688  } elseif ( $targetUser->getId() == $editor->getId() ) {
689  wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" );
690  } elseif ( $targetUser->getOption( 'enotifusertalkpages' )
691  && ( !$minorEdit || $targetUser->getOption( 'enotifminoredits' ) )
692  ) {
693  if ( !$targetUser->isEmailConfirmed() ) {
694  wfDebug( __METHOD__ . ": talk page owner doesn't have validated email\n" );
695  } elseif ( !wfRunHooks( 'AbortTalkPageEmailNotification', array( $targetUser, $title ) ) ) {
696  wfDebug( __METHOD__ . ": talk page update notification is aborted for this user\n" );
697  } else {
698  wfDebug( __METHOD__ . ": sending talk page update notification\n" );
699  return true;
700  }
701  } else {
702  wfDebug( __METHOD__ . ": talk page owner doesn't want notifications\n" );
703  }
704  }
705  return false;
706  }
707 
711  private function composeCommonMailtext() {
712  global $wgPasswordSender, $wgNoReplyAddress;
713  global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress;
714  global $wgEnotifImpersonal, $wgEnotifUseRealName;
715 
716  $this->composed_common = true;
717 
718  # You as the WikiAdmin and Sysops can make use of plenty of
719  # named variables when composing your notification emails while
720  # simply editing the Meta pages
721 
722  $keys = array();
723  $postTransformKeys = array();
724  $pageTitleUrl = $this->title->getCanonicalURL();
725  $pageTitle = $this->title->getPrefixedText();
726 
727  if ( $this->oldid ) {
728  // Always show a link to the diff which triggered the mail. See bug 32210.
729  $keys['$NEWPAGE'] = "\n\n" . wfMessage( 'enotif_lastdiff',
730  $this->title->getCanonicalURL( array( 'diff' => 'next', 'oldid' => $this->oldid ) ) )
731  ->inContentLanguage()->text();
732 
733  if ( !$wgEnotifImpersonal ) {
734  // For personal mail, also show a link to the diff of all changes
735  // since last visited.
736  $keys['$NEWPAGE'] .= "\n\n" . wfMessage( 'enotif_lastvisited',
737  $this->title->getCanonicalURL( array( 'diff' => '0', 'oldid' => $this->oldid ) ) )
738  ->inContentLanguage()->text();
739  }
740  $keys['$OLDID'] = $this->oldid;
741  // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility.
742  $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text();
743  } else {
744  # clear $OLDID placeholder in the message template
745  $keys['$OLDID'] = '';
746  $keys['$NEWPAGE'] = '';
747  // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility.
748  $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text();
749  }
750 
751  $keys['$PAGETITLE'] = $this->title->getPrefixedText();
752  $keys['$PAGETITLE_URL'] = $this->title->getCanonicalURL();
753  $keys['$PAGEMINOREDIT'] = $this->minorEdit ?
754  wfMessage( 'minoredit' )->inContentLanguage()->text() : '';
755  $keys['$UNWATCHURL'] = $this->title->getCanonicalURL( 'action=unwatch' );
756 
757  if ( $this->editor->isAnon() ) {
758  # real anon (user:xxx.xxx.xxx.xxx)
759  $keys['$PAGEEDITOR'] = wfMessage( 'enotif_anon_editor', $this->editor->getName() )
760  ->inContentLanguage()->text();
761  $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' )->inContentLanguage()->text();
762 
763  } else {
764  $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? $this->editor->getRealName() : $this->editor->getName();
765  $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $this->editor->getName() );
766  $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalURL();
767  }
768 
769  $keys['$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalURL();
770  $keys['$HELPPAGE'] = wfExpandUrl( Skin::makeInternalOrExternalUrl( wfMessage( 'helppage' )->inContentLanguage()->text() ) );
771 
772  # Replace this after transforming the message, bug 35019
773  $postTransformKeys['$PAGESUMMARY'] = $this->summary == '' ? ' - ' : $this->summary;
774 
775  // Now build message's subject and body
776 
777  // Messages:
778  // enotif_subject_deleted, enotif_subject_created, enotif_subject_moved,
779  // enotif_subject_restored, enotif_subject_changed
780  $this->subject = wfMessage( 'enotif_subject_' . $this->pageStatus )->inContentLanguage()
781  ->params( $pageTitle, $keys['$PAGEEDITOR'] )->text();
782 
783  // Messages:
784  // enotif_body_intro_deleted, enotif_body_intro_created, enotif_body_intro_moved,
785  // enotif_body_intro_restored, enotif_body_intro_changed
786  $keys['$PAGEINTRO'] = wfMessage( 'enotif_body_intro_' . $this->pageStatus )
787  ->inContentLanguage()->params( $pageTitle, $keys['$PAGEEDITOR'], $pageTitleUrl )
788  ->text();
789 
790  $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain();
791  $body = strtr( $body, $keys );
792  $body = MessageCache::singleton()->transform( $body, false, null, $this->title );
793  $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 );
794 
795  # Reveal the page editor's address as REPLY-TO address only if
796  # the user has not opted-out and the option is enabled at the
797  # global configuration level.
798  $adminAddress = new MailAddress( $wgPasswordSender,
799  wfMessage( 'emailsender' )->inContentLanguage()->text() );
800  if ( $wgEnotifRevealEditorAddress
801  && ( $this->editor->getEmail() != '' )
802  && $this->editor->getOption( 'enotifrevealaddr' )
803  ) {
804  $editorAddress = new MailAddress( $this->editor );
805  if ( $wgEnotifFromEditor ) {
806  $this->from = $editorAddress;
807  } else {
808  $this->from = $adminAddress;
809  $this->replyto = $editorAddress;
810  }
811  } else {
812  $this->from = $adminAddress;
813  $this->replyto = new MailAddress( $wgNoReplyAddress );
814  }
815  }
816 
824  function compose( $user ) {
825  global $wgEnotifImpersonal;
826 
827  if ( !$this->composed_common ) {
828  $this->composeCommonMailtext();
829  }
830 
831  if ( $wgEnotifImpersonal ) {
832  $this->mailTargets[] = new MailAddress( $user );
833  } else {
834  $this->sendPersonalised( $user );
835  }
836  }
837 
841  function sendMails() {
842  global $wgEnotifImpersonal;
843  if ( $wgEnotifImpersonal ) {
844  $this->sendImpersonal( $this->mailTargets );
845  }
846  }
847 
857  function sendPersonalised( $watchingUser ) {
858  global $wgContLang, $wgEnotifUseRealName;
859  // From the PHP manual:
860  // Note: The to parameter cannot be an address in the form of "Something <someone@example.com>".
861  // The mail command will not parse this properly while talking with the MTA.
862  $to = new MailAddress( $watchingUser );
863 
864  # $PAGEEDITDATE is the time and date of the page change
865  # expressed in terms of individual local time of the notification
866  # recipient, i.e. watching user
867  $body = str_replace(
868  array( '$WATCHINGUSERNAME',
869  '$PAGEEDITDATE',
870  '$PAGEEDITTIME' ),
871  array( $wgEnotifUseRealName ? $watchingUser->getRealName() : $watchingUser->getName(),
872  $wgContLang->userDate( $this->timestamp, $watchingUser ),
873  $wgContLang->userTime( $this->timestamp, $watchingUser ) ),
874  $this->body );
875 
876  return UserMailer::send( $to, $this->from, $this->subject, $body, $this->replyto );
877  }
878 
885  function sendImpersonal( $addresses ) {
887 
888  if ( empty( $addresses ) ) {
889  return null;
890  }
891 
892  $body = str_replace(
893  array( '$WATCHINGUSERNAME',
894  '$PAGEEDITDATE',
895  '$PAGEEDITTIME' ),
896  array( wfMessage( 'enotif_impersonal_salutation' )->inContentLanguage()->text(),
897  $wgContLang->date( $this->timestamp, false, false ),
898  $wgContLang->time( $this->timestamp, false, false ) ),
899  $this->body );
900 
901  return UserMailer::send( $addresses, $this->from, $this->subject, $body, $this->replyto );
902  }
903 
904 } # end of class EmailNotification
EmailNotification\$summary
$summary
Definition: UserMailer.php:477
UserMailer\send
static send( $to, $from, $subject, $body, $replyto=null, $contentType='text/plain;charset=UTF-8')
This function will perform a direct (authenticated) login to a SMTP Server to use for mail relaying i...
Definition: UserMailer.php:163
UserMailer\sanitizeHeaderValue
static sanitizeHeaderValue( $val)
Strips bad characters from a header value to prevent PHP mail header injection attacks.
Definition: UserMailer.php:402
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
of
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
Definition: globals.txt:10
EmailNotification\composeCommonMailtext
composeCommonMailtext()
Generate the generic "this page has been changed" e-mail text.
Definition: UserMailer.php:709
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
User\getId
getId()
Get the user's ID.
Definition: User.php:1852
$mime
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string $mime
Definition: hooks.txt:2578
UserMailer\quotedPrintable
static quotedPrintable( $string, $charset='')
Converts a string into quoted-printable format.
Definition: UserMailer.php:430
EmailNotification\notifyOnPageChange
notifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid=false, $pageStatus='changed')
Send emails corresponding to the user $editor editing the page $title.
Definition: UserMailer.php:502
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3706
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
EmailNotification\sendMails
sendMails()
Send any queued mails.
Definition: UserMailer.php:839
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1530
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2434
$from
$from
Definition: importImages.php:90
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:35
$params
$params
Definition: styleTest.css.php:40
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:389
EmailNotification\$body
$body
Definition: UserMailer.php:476
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
EmailNotification\$replyto
$replyto
Definition: UserMailer.php:476
UserMailer\sendWithPear
static sendWithPear( $mailer, $dest, $headers, $body)
Send mail using a PEAR mailer.
Definition: UserMailer.php:96
UserMailer\errorHandler
static errorHandler( $code, $string)
Set the mail error message in self::$mErrorString.
Definition: UserMailer.php:393
MailAddress\toString
toString()
Return formatted and quoted address to insert into SMTP headers.
Definition: UserMailer.php:54
SpecialPage\getSafeTitleFor
static getSafeTitleFor( $name, $subpage=false)
Get a localised Title object for a page name with a possibly unvalidated subpage.
Definition: SpecialPage.php:87
MailAddress
Stores a single person's name and email address.
Definition: UserMailer.php:32
title
to move a page</td >< td > &*You are moving the page across *A non empty talk page already exists under the new or *You uncheck the box below In those you will have to move or merge the page manually if desired</td >< td > be sure to &You are responsible for making sure that links continue to point where they are supposed to go Note that the page will &a page at the new title
Definition: All_system_messages.txt:2703
wfParseUrl
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
Definition: GlobalFunctions.php:802
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:857
MWException
MediaWiki exception.
Definition: MWException.php:26
$out
$out
Definition: UtfNormalGenerate.php:167
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2464
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:880
EmailNotification\$oldid
$oldid
Definition: UserMailer.php:477
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4058
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
EmailNotification\$minorEdit
$minorEdit
Definition: UserMailer.php:477
UserArray\newFromIDs
static newFromIDs( $ids)
Definition: UserArray.php:43
UserMailer\arrayToHeaderString
static arrayToHeaderString( $headers, $endl="\n")
Creates a single string from an associative array.
Definition: UserMailer.php:120
EmailNotification\compose
compose( $user)
Compose a mail to a given user and either queue it for sending, or send it now, depending on settings...
Definition: UserMailer.php:822
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:101
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3660
EmailNotification\$timestamp
$timestamp
Definition: UserMailer.php:477
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:82
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
UserMailer
Collection of static functions for sending mail.
Definition: UserMailer.php:83
$value
$value
Definition: styleTest.css.php:45
MailAddress\__construct
__construct( $address, $name=null, $realName=null)
Definition: UserMailer.php:38
EmailNotification\actuallyNotifyOnPageChange
actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers, $pageStatus='changed')
Immediate version of notifyOnPageChange().
Definition: UserMailer.php:599
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2571
mail
address of the mail
Definition: All_system_messages.txt:1386
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
wfIniGetBool
wfIniGetBool( $setting)
Safety wrapper around ini_get() for boolean settings.
Definition: GlobalFunctions.php:2732
UserMailer\makeMsgId
static makeMsgId()
Create a value suitable for the MessageId Header.
Definition: UserMailer.php:135
Title
Represents a title within MediaWiki.
Definition: Title.php:35
EmailNotification\canSendUserTalkEmail
canSendUserTalkEmail( $editor, $title, $minorEdit)
Definition: UserMailer.php:677
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:42
EmailNotification\$mailTargets
$mailTargets
Definition: UserMailer.php:478
EmailNotification\sendImpersonal
sendImpersonal( $addresses)
Same as sendPersonalised but does impersonal mail suitable for bulk mailing.
Definition: UserMailer.php:883
JobQueueGroup\singleton
static singleton( $wiki=false)
Definition: JobQueueGroup.php:61
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
EmailNotification\$title
Title $title
Definition: UserMailer.php:482
$keys
$keys
Definition: testCompression.php:63
EmailNotification\sendPersonalised
sendPersonalised( $watchingUser)
Does the per-user customizations to a notification e-mail (name, timestamp in proper timezone,...
Definition: UserMailer.php:855
from
Please log in again after you receive it</td >< td > s a saved copy from
Definition: All_system_messages.txt:3297
EmailNotification\$editor
User $editor
Definition: UserMailer.php:486
name
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at name
Definition: design.txt:12
UserMailer\quotedPrintableCallback
static quotedPrintableCallback( $matches)
Definition: UserMailer.php:450
EmailNotification
This module processes the email notifications when the current page is changed.
Definition: UserMailer.php:475
UserMailer\rfc822Phrase
static rfc822Phrase( $phrase)
Converts a string into a valid RFC 822 "phrase", such as is used for the sender name.
Definition: UserMailer.php:411
EmailNotification\$subject
$subject
Definition: UserMailer.php:476
EmailNotification\$composed_common
$composed_common
Definition: UserMailer.php:477
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
EnotifNotifyJob
Job for email notification mails.
Definition: EnotifNotifyJob.php:29
$res
$res
Definition: database.txt:21
MWTimestamp\getLocalInstance
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
Definition: MWTimestamp.php:373
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:1876
EmailNotification\$pageStatus
$pageStatus
Definition: UserMailer.php:477
Title\getText
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:839
MailAddress\__toString
__toString()
Definition: UserMailer.php:75
EmailNotification\$from
$from
Definition: UserMailer.php:476
Skin\makeInternalOrExternalUrl
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition: Skin.php:1158
UserMailer\$mErrorString
static $mErrorString
Definition: UserMailer.php:84
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:544
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
User\isAllowed
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3030
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63