59 private const USER_TALK =
'user_talk';
63 private const WATCHLIST =
'watchlist';
67 private const ALL_CHANGES =
'all_changes';
70 protected $subject =
'';
85 protected $summary =
'';
94 protected $composed_common =
false;
97 protected $pageStatus =
'';
100 protected $mailTargets = [];
119 return $this->pageStatus;
144 $pageStatus =
'changed'
150 $mwServices = MediaWikiServices::getInstance();
151 $config = $mwServices->getMainConfig();
155 if ( $config->get( MainConfigNames::EnotifWatchlist ) ||
156 $config->get( MainConfigNames::ShowUpdatedMarker ) ) {
157 $watchers = $mwServices->getWatchedItemStore()->updateNotificationTimestamp(
169 if ( $watchers === [] &&
170 !count( $config->get( MainConfigNames::UsersNotifiedOnAllChanges ) ) ) {
174 ( $config->get( MainConfigNames::EnotifMinorEdits ) &&
175 !$editor->isAllowed(
'nominornewtalk' ) )
178 if ( $config->get( MainConfigNames::EnotifUserTalk )
180 && $this->canSendUserTalkEmail( $editor->getUser(),
$title, $minorEdit )
191 'editor' => $editor->getUser()->
getName(),
192 'editorID' => $editor->getUser()->
getId(),
193 'timestamp' => $timestamp,
194 'summary' => $summary,
195 'minorEdit' => $minorEdit,
197 'watchers' => $watchers,
198 'pageStatus' => $pageStatus
230 $pageStatus =
'changed'
232 # we use $wgPasswordSender as sender's address
234 $mwServices = MediaWikiServices::getInstance();
235 $messageCache = $mwServices->getMessageCache();
236 $config = $mwServices->getMainConfig();
238 # The following code is only run, if several conditions are met:
239 # 1. EmailNotification for pages (other than user_talk pages) must be enabled
240 # 2. minor edits (changes) are only regarded if the global flag indicates so
245 $this->timestamp = $timestamp;
246 $this->summary = $summary;
247 $this->minorEdit = $minorEdit;
248 $this->oldid = $oldid;
249 $this->editor = MediaWikiServices::getInstance()->getUserFactory()->newFromAuthority( $editor );
250 $this->composed_common =
false;
251 $this->pageStatus = $pageStatus;
253 $formattedPageStatus = [
'deleted',
'created',
'moved',
'restored',
'changed' ];
255 Hooks::runner()->onUpdateUserMailerFormattedPageStatus( $formattedPageStatus );
256 if ( !in_array( $this->pageStatus, $formattedPageStatus ) ) {
257 throw new MWException(
'Not a valid page status!' );
263 ( $config->get( MainConfigNames::EnotifMinorEdits ) &&
264 !$editor->
isAllowed(
'nominornewtalk' ) )
266 if ( $config->get( MainConfigNames::EnotifUserTalk )
268 && $this->canSendUserTalkEmail( $editor->
getUser(),
$title, $minorEdit )
271 $this->compose( $targetUser, self::USER_TALK, $messageCache );
272 $userTalkId = $targetUser->getId();
275 if ( $config->get( MainConfigNames::EnotifWatchlist ) ) {
276 $userOptionsLookup = $mwServices->getUserOptionsLookup();
280 foreach ( $userArray as $watchingUser ) {
281 if ( $userOptionsLookup->getOption( $watchingUser,
'enotifwatchlistpages' )
282 && ( !$minorEdit || $userOptionsLookup->getOption( $watchingUser,
'enotifminoredits' ) )
283 && $watchingUser->isEmailConfirmed()
284 && $watchingUser->getId() != $userTalkId
285 && !in_array( $watchingUser->getName(),
286 $config->get( MainConfigNames::UsersNotifiedOnAllChanges ) )
289 && !( $config->get( MainConfigNames::BlockDisablesLogin ) &&
290 $watchingUser->getBlock() )
291 && Hooks::runner()->onSendWatchlistEmailNotification( $watchingUser,
$title, $this )
293 $this->compose( $watchingUser, self::WATCHLIST, $messageCache );
299 foreach ( $config->get( MainConfigNames::UsersNotifiedOnAllChanges ) as $name ) {
300 if ( $editor->
getUser()->getName() == $name ) {
305 if ( $user instanceof
User ) {
306 $this->compose( $user, self::ALL_CHANGES, $messageCache );
319 $services = MediaWikiServices::getInstance();
320 $config = $services->getMainConfig();
323 if ( !$config->get( MainConfigNames::EnotifUserTalk ) || !$isUserTalkPage ) {
327 $userOptionsLookup = $services->getUserOptionsLookup();
330 if ( !$targetUser || $targetUser->isAnon() ) {
331 wfDebug( __METHOD__ .
": user talk page edited, but user does not exist" );
332 } elseif ( $targetUser->getId() == $editor->
getId() ) {
333 wfDebug( __METHOD__ .
": user edited their own talk page, no notification sent" );
334 } elseif ( $config->get( MainConfigNames::BlockDisablesLogin ) &&
335 $targetUser->getBlock() ) {
338 wfDebug( __METHOD__ .
": talk page owner is blocked and cannot login, no notification sent" );
339 } elseif ( $userOptionsLookup->getOption( $targetUser,
'enotifusertalkpages' )
340 && ( !$minorEdit || $userOptionsLookup->getOption( $targetUser,
'enotifminoredits' ) )
342 if ( !$targetUser->isEmailConfirmed() ) {
343 wfDebug( __METHOD__ .
": talk page owner doesn't have validated email" );
344 } elseif ( !Hooks::runner()->onAbortTalkPageEmailNotification( $targetUser,
$title ) ) {
345 wfDebug( __METHOD__ .
": talk page update notification is aborted for this user" );
347 wfDebug( __METHOD__ .
": sending talk page update notification" );
351 wfDebug( __METHOD__ .
": talk page owner doesn't want notifications" );
360 private function composeCommonMailtext(
MessageCache $messageCache ) {
361 $services = MediaWikiServices::getInstance();
362 $config = $services->getMainConfig();
363 $userOptionsLookup = $services->getUserOptionsLookup();
365 $this->composed_common =
true;
367 # You as the WikiAdmin and Sysops can make use of plenty of
368 # named variables when composing your notification emails while
369 # simply editing the Meta pages
372 $postTransformKeys = [];
373 $pageTitleUrl = $this->title->getCanonicalURL();
374 $pageTitle = $this->title->getPrefixedText();
376 if ( $this->oldid ) {
380 $this->title->getCanonicalURL( [
'diff' =>
'next',
'oldid' => $this->oldid ] )
381 )->inContentLanguage()->text();
383 if ( !$config->get( MainConfigNames::EnotifImpersonal ) ) {
387 'enotif_lastvisited',
388 $this->title->getCanonicalURL( [
'diff' =>
'0',
'oldid' => $this->oldid ] )
389 )->inContentLanguage()->text();
391 $keys[
'$OLDID'] = $this->oldid;
393 $keys[
'$CHANGEDORCREATED'] =
wfMessage(
'changed' )->inContentLanguage()->text();
395 # clear $OLDID placeholder in the message template
396 $keys[
'$OLDID'] =
'';
397 $keys[
'$NEWPAGE'] =
'';
399 $keys[
'$CHANGEDORCREATED'] =
wfMessage(
'created' )->inContentLanguage()->text();
402 $keys[
'$PAGETITLE'] = $this->title->getPrefixedText();
403 $keys[
'$PAGETITLE_URL'] = $this->title->getCanonicalURL();
404 $keys[
'$PAGEMINOREDIT'] = $this->minorEdit ?
405 "\n\n" .
wfMessage(
'enotif_minoredit' )->inContentLanguage()->text() :
407 $keys[
'$UNWATCHURL'] = $this->title->getCanonicalURL(
'action=unwatch' );
409 if ( !$this->editor->isRegistered() ) {
410 # real anon (user:xxx.xxx.xxx.xxx)
411 $keys[
'$PAGEEDITOR'] =
wfMessage(
'enotif_anon_editor', $this->editor->getName() )
412 ->inContentLanguage()->text();
413 $keys[
'$PAGEEDITOR_EMAIL'] =
wfMessage(
'noemailtitle' )->inContentLanguage()->text();
416 $keys[
'$PAGEEDITOR'] = $config->get( MainConfigNames::EnotifUseRealName ) &&
417 $this->editor->getRealName() !==
''
418 ? $this->editor->getRealName() : $this->editor->getName();
420 $keys[
'$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalURL();
423 $keys[
'$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalURL();
428 # Replace this after transforming the message, T37019
429 $postTransformKeys[
'$PAGESUMMARY'] = $this->summary ==
'' ?
' - ' : $this->summary;
436 $this->subject =
wfMessage(
'enotif_subject_' . $this->pageStatus )->inContentLanguage()
437 ->params( $pageTitle,
$keys[
'$PAGEEDITOR'] )->text();
442 $keys[
'$PAGEINTRO'] =
wfMessage(
'enotif_body_intro_' . $this->pageStatus )
443 ->inContentLanguage()
444 ->params( $pageTitle,
$keys[
'$PAGEEDITOR'], $pageTitleUrl )
447 $body =
wfMessage(
'enotif_body' )->inContentLanguage()->plain();
448 $body = strtr( $body,
$keys );
449 $body = $messageCache->
transform( $body,
false,
null, $this->title );
450 $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 );
452 # Reveal the page editor's address as REPLY-TO address only if
453 # the user has not opted-out and the option is enabled at the
454 # global configuration level.
456 $config->get( MainConfigNames::PasswordSender ),
457 wfMessage(
'emailsender' )->inContentLanguage()->text()
459 if ( $config->get( MainConfigNames::EnotifRevealEditorAddress )
460 && ( $this->editor->getEmail() !=
'' )
461 && $userOptionsLookup->getOption( $this->editor,
'enotifrevealaddr' )
464 if ( $config->get( MainConfigNames::EnotifFromEditor ) ) {
465 $this->from = $editorAddress;
467 $this->from = $adminAddress;
468 $this->replyto = $editorAddress;
471 $this->from = $adminAddress;
473 $config->get( MainConfigNames::NoReplyAddress )
488 if ( !$this->composed_common ) {
489 $this->composeCommonMailtext( $messageCache );
492 if ( MediaWikiServices::getInstance()->getMainConfig()
493 ->
get( MainConfigNames::EnotifImpersonal ) ) {
496 $this->sendPersonalised( $user,
$source );
503 private function sendMails() {
504 if ( MediaWikiServices::getInstance()->getMainConfig()
505 ->
get( MainConfigNames::EnotifImpersonal ) ) {
506 $this->sendImpersonal( $this->mailTargets );
527 # $PAGEEDITDATE is the time and date of the page change
528 # expressed in terms of individual local time of the notification
529 # recipient, i.e. watching user
530 $mwServices = MediaWikiServices::getInstance();
531 $contLang = $mwServices->getContentLanguage();
532 $watchingUserName = (
533 $mwServices->getMainConfig()->get( MainConfigNames::EnotifUseRealName ) &&
544 $contLang->userDate( $this->timestamp, $watchingUser->
getUser() ),
545 $contLang->userTime( $this->timestamp, $watchingUser->
getUser() )
551 if (
$source === self::WATCHLIST ) {
552 $headers[
'List-Help'] =
'https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Watchlist';
556 'replyTo' => $this->replyto,
557 'headers' => $headers,
567 private function sendImpersonal( $addresses ) {
568 if ( empty( $addresses ) ) {
572 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
580 wfMessage(
'enotif_impersonal_salutation' )->inContentLanguage()->text(),
581 $contLang->date( $this->timestamp,
false,
false ),
582 $contLang->time( $this->timestamp,
false,
false )
588 'replyTo' => $this->replyto,
static getSafeTitleFor( $name, $subpage=false)
Get a localised Title object for a page name with a possibly unvalidated subpage.