This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Classes used to send e-mails |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @author <[email protected]> |
||
22 | * @author <[email protected]> |
||
23 | * @author Tim Starling |
||
24 | * @author Luke Welling [email protected] |
||
25 | */ |
||
26 | use MediaWiki\Linker\LinkTarget; |
||
27 | |||
28 | use MediaWiki\MediaWikiServices; |
||
29 | |||
30 | /** |
||
31 | * This module processes the email notifications when the current page is |
||
32 | * changed. It looks up the table watchlist to find out which users are watching |
||
33 | * that page. |
||
34 | * |
||
35 | * The current implementation sends independent emails to each watching user for |
||
36 | * the following reason: |
||
37 | * |
||
38 | * - Each watching user will be notified about the page edit time expressed in |
||
39 | * his/her local time (UTC is shown additionally). To achieve this, we need to |
||
40 | * find the individual timeoffset of each watching user from the preferences.. |
||
41 | * |
||
42 | * Suggested improvement to slack down the number of sent emails: We could think |
||
43 | * of sending out bulk mails (bcc:user1,user2...) for all these users having the |
||
44 | * same timeoffset in their preferences. |
||
45 | * |
||
46 | * Visit the documentation pages under http://meta.wikipedia.com/Enotif |
||
47 | */ |
||
48 | class EmailNotification { |
||
49 | |||
50 | /** |
||
51 | * Notification is due to user's user talk being edited |
||
52 | */ |
||
53 | const USER_TALK = 'user_talk'; |
||
54 | /** |
||
55 | * Notification is due to a watchlisted page being edited |
||
56 | */ |
||
57 | const WATCHLIST = 'watchlist'; |
||
58 | /** |
||
59 | * Notification because user is notified for all changes |
||
60 | */ |
||
61 | const ALL_CHANGES = 'all_changes'; |
||
62 | |||
63 | protected $subject, $body, $replyto, $from; |
||
0 ignored issues
–
show
|
|||
64 | protected $timestamp, $summary, $minorEdit, $oldid, $composed_common, $pageStatus; |
||
65 | protected $mailTargets = []; |
||
66 | |||
67 | /** |
||
68 | * @var Title |
||
69 | */ |
||
70 | protected $title; |
||
71 | |||
72 | /** |
||
73 | * @var User |
||
74 | */ |
||
75 | protected $editor; |
||
76 | |||
77 | /** |
||
78 | * @deprecated since 1.27 use WatchedItemStore::updateNotificationTimestamp directly |
||
79 | * |
||
80 | * @param User $editor The editor that triggered the update. Their notification |
||
81 | * timestamp will not be updated(they have already seen it) |
||
82 | * @param LinkTarget $linkTarget The link target of the title to update timestamps for |
||
83 | * @param string $timestamp Set the update timestamp to this value |
||
84 | * |
||
85 | * @return int[] Array of user IDs |
||
86 | */ |
||
87 | public static function updateWatchlistTimestamp( |
||
88 | User $editor, |
||
89 | LinkTarget $linkTarget, |
||
90 | $timestamp |
||
91 | ) { |
||
92 | // wfDeprecated( __METHOD__, '1.27' ); |
||
93 | $config = RequestContext::getMain()->getConfig(); |
||
94 | if ( !$config->get( 'EnotifWatchlist' ) && !$config->get( 'ShowUpdatedMarker' ) ) { |
||
95 | return []; |
||
96 | } |
||
97 | return MediaWikiServices::getInstance()->getWatchedItemStore()->updateNotificationTimestamp( |
||
98 | $editor, |
||
99 | $linkTarget, |
||
100 | $timestamp |
||
101 | ); |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Send emails corresponding to the user $editor editing the page $title. |
||
106 | * |
||
107 | * May be deferred via the job queue. |
||
108 | * |
||
109 | * @param User $editor |
||
110 | * @param Title $title |
||
111 | * @param string $timestamp |
||
112 | * @param string $summary |
||
113 | * @param bool $minorEdit |
||
114 | * @param bool $oldid (default: false) |
||
115 | * @param string $pageStatus (default: 'changed') |
||
116 | */ |
||
117 | public function notifyOnPageChange( $editor, $title, $timestamp, $summary, |
||
118 | $minorEdit, $oldid = false, $pageStatus = 'changed' |
||
119 | ) { |
||
120 | global $wgEnotifMinorEdits, $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk; |
||
121 | |||
122 | if ( $title->getNamespace() < 0 ) { |
||
123 | return; |
||
124 | } |
||
125 | |||
126 | // update wl_notificationtimestamp for watchers |
||
127 | $config = RequestContext::getMain()->getConfig(); |
||
128 | $watchers = []; |
||
129 | if ( $config->get( 'EnotifWatchlist' ) || $config->get( 'ShowUpdatedMarker' ) ) { |
||
130 | $watchers = MediaWikiServices::getInstance()->getWatchedItemStore()->updateNotificationTimestamp( |
||
131 | $editor, |
||
132 | $title, |
||
133 | $timestamp |
||
134 | ); |
||
135 | } |
||
136 | |||
137 | $sendEmail = true; |
||
138 | // $watchers deals with $wgEnotifWatchlist. |
||
139 | // If nobody is watching the page, and there are no users notified on all changes |
||
140 | // don't bother creating a job/trying to send emails, unless it's a |
||
141 | // talk page with an applicable notification. |
||
142 | if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) { |
||
143 | $sendEmail = false; |
||
144 | // Only send notification for non minor edits, unless $wgEnotifMinorEdits |
||
145 | if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { |
||
146 | $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); |
||
147 | if ( $wgEnotifUserTalk |
||
148 | && $isUserTalkPage |
||
149 | && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) |
||
150 | ) { |
||
151 | $sendEmail = true; |
||
152 | } |
||
153 | } |
||
154 | } |
||
155 | |||
156 | if ( $sendEmail ) { |
||
157 | JobQueueGroup::singleton()->lazyPush( new EnotifNotifyJob( |
||
158 | $title, |
||
159 | [ |
||
160 | 'editor' => $editor->getName(), |
||
161 | 'editorID' => $editor->getId(), |
||
162 | 'timestamp' => $timestamp, |
||
163 | 'summary' => $summary, |
||
164 | 'minorEdit' => $minorEdit, |
||
165 | 'oldid' => $oldid, |
||
166 | 'watchers' => $watchers, |
||
167 | 'pageStatus' => $pageStatus |
||
168 | ] |
||
169 | ) ); |
||
170 | } |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Immediate version of notifyOnPageChange(). |
||
175 | * |
||
176 | * Send emails corresponding to the user $editor editing the page $title. |
||
177 | * |
||
178 | * @note Do not call directly. Use notifyOnPageChange so that wl_notificationtimestamp is updated. |
||
179 | * @param User $editor |
||
180 | * @param Title $title |
||
181 | * @param string $timestamp Edit timestamp |
||
182 | * @param string $summary Edit summary |
||
183 | * @param bool $minorEdit |
||
184 | * @param int $oldid Revision ID |
||
185 | * @param array $watchers Array of user IDs |
||
186 | * @param string $pageStatus |
||
187 | * @throws MWException |
||
188 | */ |
||
189 | public function actuallyNotifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, |
||
190 | $oldid, $watchers, $pageStatus = 'changed' ) { |
||
191 | # we use $wgPasswordSender as sender's address |
||
192 | global $wgUsersNotifiedOnAllChanges; |
||
193 | global $wgEnotifWatchlist, $wgBlockDisablesLogin; |
||
194 | global $wgEnotifMinorEdits, $wgEnotifUserTalk; |
||
195 | |||
196 | # The following code is only run, if several conditions are met: |
||
197 | # 1. EmailNotification for pages (other than user_talk pages) must be enabled |
||
198 | # 2. minor edits (changes) are only regarded if the global flag indicates so |
||
199 | |||
200 | $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); |
||
201 | |||
202 | $this->title = $title; |
||
203 | $this->timestamp = $timestamp; |
||
204 | $this->summary = $summary; |
||
205 | $this->minorEdit = $minorEdit; |
||
206 | $this->oldid = $oldid; |
||
207 | $this->editor = $editor; |
||
208 | $this->composed_common = false; |
||
209 | $this->pageStatus = $pageStatus; |
||
210 | |||
211 | $formattedPageStatus = [ 'deleted', 'created', 'moved', 'restored', 'changed' ]; |
||
212 | |||
213 | Hooks::run( 'UpdateUserMailerFormattedPageStatus', [ &$formattedPageStatus ] ); |
||
214 | if ( !in_array( $this->pageStatus, $formattedPageStatus ) ) { |
||
215 | throw new MWException( 'Not a valid page status!' ); |
||
216 | } |
||
217 | |||
218 | $userTalkId = false; |
||
219 | |||
220 | if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) { |
||
221 | if ( $wgEnotifUserTalk |
||
222 | && $isUserTalkPage |
||
223 | && $this->canSendUserTalkEmail( $editor, $title, $minorEdit ) |
||
224 | ) { |
||
225 | $targetUser = User::newFromName( $title->getText() ); |
||
226 | $this->compose( $targetUser, self::USER_TALK ); |
||
227 | $userTalkId = $targetUser->getId(); |
||
228 | } |
||
229 | |||
230 | if ( $wgEnotifWatchlist ) { |
||
231 | // Send updates to watchers other than the current editor |
||
232 | // and don't send to watchers who are blocked and cannot login |
||
233 | $userArray = UserArray::newFromIDs( $watchers ); |
||
234 | foreach ( $userArray as $watchingUser ) { |
||
235 | if ( $watchingUser->getOption( 'enotifwatchlistpages' ) |
||
236 | && ( !$minorEdit || $watchingUser->getOption( 'enotifminoredits' ) ) |
||
237 | && $watchingUser->isEmailConfirmed() |
||
238 | && $watchingUser->getId() != $userTalkId |
||
239 | && !in_array( $watchingUser->getName(), $wgUsersNotifiedOnAllChanges ) |
||
240 | && !( $wgBlockDisablesLogin && $watchingUser->isBlocked() ) |
||
241 | ) { |
||
242 | if ( Hooks::run( 'SendWatchlistEmailNotification', [ $watchingUser, $title, $this ] ) ) { |
||
243 | $this->compose( $watchingUser, self::WATCHLIST ); |
||
244 | } |
||
245 | } |
||
246 | } |
||
247 | } |
||
248 | } |
||
249 | |||
250 | foreach ( $wgUsersNotifiedOnAllChanges as $name ) { |
||
251 | if ( $editor->getName() == $name ) { |
||
252 | // No point notifying the user that actually made the change! |
||
253 | continue; |
||
254 | } |
||
255 | $user = User::newFromName( $name ); |
||
256 | $this->compose( $user, self::ALL_CHANGES ); |
||
257 | } |
||
258 | |||
259 | $this->sendMails(); |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * @param User $editor |
||
264 | * @param Title $title |
||
265 | * @param bool $minorEdit |
||
266 | * @return bool |
||
267 | */ |
||
268 | private function canSendUserTalkEmail( $editor, $title, $minorEdit ) { |
||
269 | global $wgEnotifUserTalk, $wgBlockDisablesLogin; |
||
270 | $isUserTalkPage = ( $title->getNamespace() == NS_USER_TALK ); |
||
271 | |||
272 | if ( $wgEnotifUserTalk && $isUserTalkPage ) { |
||
273 | $targetUser = User::newFromName( $title->getText() ); |
||
274 | |||
275 | if ( !$targetUser || $targetUser->isAnon() ) { |
||
276 | wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" ); |
||
277 | } elseif ( $targetUser->getId() == $editor->getId() ) { |
||
278 | wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" ); |
||
279 | } elseif ( $wgBlockDisablesLogin && $targetUser->isBlocked() ) { |
||
280 | wfDebug( __METHOD__ . ": talk page owner is blocked and cannot login, no notification sent\n" ); |
||
281 | } elseif ( $targetUser->getOption( 'enotifusertalkpages' ) |
||
282 | && ( !$minorEdit || $targetUser->getOption( 'enotifminoredits' ) ) |
||
283 | ) { |
||
284 | if ( !$targetUser->isEmailConfirmed() ) { |
||
285 | wfDebug( __METHOD__ . ": talk page owner doesn't have validated email\n" ); |
||
286 | } elseif ( !Hooks::run( 'AbortTalkPageEmailNotification', [ $targetUser, $title ] ) ) { |
||
287 | wfDebug( __METHOD__ . ": talk page update notification is aborted for this user\n" ); |
||
288 | } else { |
||
289 | wfDebug( __METHOD__ . ": sending talk page update notification\n" ); |
||
290 | return true; |
||
291 | } |
||
292 | } else { |
||
293 | wfDebug( __METHOD__ . ": talk page owner doesn't want notifications\n" ); |
||
294 | } |
||
295 | } |
||
296 | return false; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Generate the generic "this page has been changed" e-mail text. |
||
301 | */ |
||
302 | private function composeCommonMailtext() { |
||
303 | global $wgPasswordSender, $wgNoReplyAddress; |
||
304 | global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress; |
||
305 | global $wgEnotifImpersonal, $wgEnotifUseRealName; |
||
306 | |||
307 | $this->composed_common = true; |
||
308 | |||
309 | # You as the WikiAdmin and Sysops can make use of plenty of |
||
310 | # named variables when composing your notification emails while |
||
311 | # simply editing the Meta pages |
||
312 | |||
313 | $keys = []; |
||
314 | $postTransformKeys = []; |
||
315 | $pageTitleUrl = $this->title->getCanonicalURL(); |
||
316 | $pageTitle = $this->title->getPrefixedText(); |
||
317 | |||
318 | if ( $this->oldid ) { |
||
319 | // Always show a link to the diff which triggered the mail. See bug 32210. |
||
320 | $keys['$NEWPAGE'] = "\n\n" . wfMessage( 'enotif_lastdiff', |
||
321 | $this->title->getCanonicalURL( [ 'diff' => 'next', 'oldid' => $this->oldid ] ) ) |
||
322 | ->inContentLanguage()->text(); |
||
323 | |||
324 | if ( !$wgEnotifImpersonal ) { |
||
325 | // For personal mail, also show a link to the diff of all changes |
||
326 | // since last visited. |
||
327 | $keys['$NEWPAGE'] .= "\n\n" . wfMessage( 'enotif_lastvisited', |
||
328 | $this->title->getCanonicalURL( [ 'diff' => '0', 'oldid' => $this->oldid ] ) ) |
||
329 | ->inContentLanguage()->text(); |
||
330 | } |
||
331 | $keys['$OLDID'] = $this->oldid; |
||
332 | // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. |
||
333 | $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text(); |
||
334 | } else { |
||
335 | # clear $OLDID placeholder in the message template |
||
336 | $keys['$OLDID'] = ''; |
||
337 | $keys['$NEWPAGE'] = ''; |
||
338 | // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. |
||
339 | $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text(); |
||
340 | } |
||
341 | |||
342 | $keys['$PAGETITLE'] = $this->title->getPrefixedText(); |
||
343 | $keys['$PAGETITLE_URL'] = $this->title->getCanonicalURL(); |
||
344 | $keys['$PAGEMINOREDIT'] = $this->minorEdit ? |
||
345 | wfMessage( 'minoredit' )->inContentLanguage()->text() : ''; |
||
346 | $keys['$UNWATCHURL'] = $this->title->getCanonicalURL( 'action=unwatch' ); |
||
347 | |||
348 | if ( $this->editor->isAnon() ) { |
||
349 | # real anon (user:xxx.xxx.xxx.xxx) |
||
350 | $keys['$PAGEEDITOR'] = wfMessage( 'enotif_anon_editor', $this->editor->getName() ) |
||
351 | ->inContentLanguage()->text(); |
||
352 | $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' )->inContentLanguage()->text(); |
||
353 | |||
354 | } else { |
||
355 | $keys['$PAGEEDITOR'] = $wgEnotifUseRealName && $this->editor->getRealName() !== '' |
||
356 | ? $this->editor->getRealName() : $this->editor->getName(); |
||
357 | $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $this->editor->getName() ); |
||
358 | $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalURL(); |
||
359 | } |
||
360 | |||
361 | $keys['$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalURL(); |
||
362 | $keys['$HELPPAGE'] = wfExpandUrl( |
||
363 | Skin::makeInternalOrExternalUrl( wfMessage( 'helppage' )->inContentLanguage()->text() ) |
||
364 | ); |
||
365 | |||
366 | # Replace this after transforming the message, bug 35019 |
||
367 | $postTransformKeys['$PAGESUMMARY'] = $this->summary == '' ? ' - ' : $this->summary; |
||
368 | |||
369 | // Now build message's subject and body |
||
370 | |||
371 | // Messages: |
||
372 | // enotif_subject_deleted, enotif_subject_created, enotif_subject_moved, |
||
373 | // enotif_subject_restored, enotif_subject_changed |
||
374 | $this->subject = wfMessage( 'enotif_subject_' . $this->pageStatus )->inContentLanguage() |
||
375 | ->params( $pageTitle, $keys['$PAGEEDITOR'] )->text(); |
||
376 | |||
377 | // Messages: |
||
378 | // enotif_body_intro_deleted, enotif_body_intro_created, enotif_body_intro_moved, |
||
379 | // enotif_body_intro_restored, enotif_body_intro_changed |
||
380 | $keys['$PAGEINTRO'] = wfMessage( 'enotif_body_intro_' . $this->pageStatus ) |
||
381 | ->inContentLanguage()->params( $pageTitle, $keys['$PAGEEDITOR'], $pageTitleUrl ) |
||
382 | ->text(); |
||
383 | |||
384 | $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain(); |
||
385 | $body = strtr( $body, $keys ); |
||
386 | $body = MessageCache::singleton()->transform( $body, false, null, $this->title ); |
||
387 | $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 ); |
||
388 | |||
389 | # Reveal the page editor's address as REPLY-TO address only if |
||
390 | # the user has not opted-out and the option is enabled at the |
||
391 | # global configuration level. |
||
392 | $adminAddress = new MailAddress( $wgPasswordSender, |
||
393 | wfMessage( 'emailsender' )->inContentLanguage()->text() ); |
||
394 | if ( $wgEnotifRevealEditorAddress |
||
395 | && ( $this->editor->getEmail() != '' ) |
||
396 | && $this->editor->getOption( 'enotifrevealaddr' ) |
||
397 | ) { |
||
398 | $editorAddress = MailAddress::newFromUser( $this->editor ); |
||
399 | if ( $wgEnotifFromEditor ) { |
||
400 | $this->from = $editorAddress; |
||
401 | } else { |
||
402 | $this->from = $adminAddress; |
||
403 | $this->replyto = $editorAddress; |
||
404 | } |
||
405 | } else { |
||
406 | $this->from = $adminAddress; |
||
407 | $this->replyto = new MailAddress( $wgNoReplyAddress ); |
||
408 | } |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Compose a mail to a given user and either queue it for sending, or send it now, |
||
413 | * depending on settings. |
||
414 | * |
||
415 | * Call sendMails() to send any mails that were queued. |
||
416 | * @param User $user |
||
417 | * @param string $source |
||
418 | */ |
||
419 | function compose( $user, $source ) { |
||
420 | global $wgEnotifImpersonal; |
||
421 | |||
422 | if ( !$this->composed_common ) { |
||
423 | $this->composeCommonMailtext(); |
||
424 | } |
||
425 | |||
426 | if ( $wgEnotifImpersonal ) { |
||
427 | $this->mailTargets[] = MailAddress::newFromUser( $user ); |
||
428 | } else { |
||
429 | $this->sendPersonalised( $user, $source ); |
||
430 | } |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * Send any queued mails |
||
435 | */ |
||
436 | function sendMails() { |
||
437 | global $wgEnotifImpersonal; |
||
438 | if ( $wgEnotifImpersonal ) { |
||
439 | $this->sendImpersonal( $this->mailTargets ); |
||
440 | } |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Does the per-user customizations to a notification e-mail (name, |
||
445 | * timestamp in proper timezone, etc) and sends it out. |
||
446 | * Returns true if the mail was sent successfully. |
||
447 | * |
||
448 | * @param User $watchingUser |
||
449 | * @param string $source |
||
450 | * @return bool |
||
451 | * @private |
||
452 | */ |
||
453 | function sendPersonalised( $watchingUser, $source ) { |
||
454 | global $wgContLang, $wgEnotifUseRealName; |
||
455 | // From the PHP manual: |
||
456 | // Note: The to parameter cannot be an address in the form of |
||
457 | // "Something <[email protected]>". The mail command will not parse |
||
458 | // this properly while talking with the MTA. |
||
459 | $to = MailAddress::newFromUser( $watchingUser ); |
||
460 | |||
461 | # $PAGEEDITDATE is the time and date of the page change |
||
462 | # expressed in terms of individual local time of the notification |
||
463 | # recipient, i.e. watching user |
||
464 | $body = str_replace( |
||
465 | [ '$WATCHINGUSERNAME', |
||
466 | '$PAGEEDITDATE', |
||
467 | '$PAGEEDITTIME' ], |
||
468 | [ $wgEnotifUseRealName && $watchingUser->getRealName() !== '' |
||
469 | ? $watchingUser->getRealName() : $watchingUser->getName(), |
||
470 | $wgContLang->userDate( $this->timestamp, $watchingUser ), |
||
471 | $wgContLang->userTime( $this->timestamp, $watchingUser ) ], |
||
472 | $this->body ); |
||
473 | |||
474 | $headers = []; |
||
475 | if ( $source === self::WATCHLIST ) { |
||
476 | $headers['List-Help'] = 'https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Watchlist'; |
||
477 | } |
||
478 | |||
479 | return UserMailer::send( $to, $this->from, $this->subject, $body, [ |
||
480 | 'replyTo' => $this->replyto, |
||
481 | 'headers' => $headers, |
||
482 | ] ); |
||
483 | } |
||
484 | |||
485 | /** |
||
486 | * Same as sendPersonalised but does impersonal mail suitable for bulk |
||
487 | * mailing. Takes an array of MailAddress objects. |
||
488 | * @param MailAddress[] $addresses |
||
489 | * @return Status|null |
||
490 | */ |
||
491 | function sendImpersonal( $addresses ) { |
||
492 | global $wgContLang; |
||
493 | |||
494 | if ( empty( $addresses ) ) { |
||
495 | return null; |
||
496 | } |
||
497 | |||
498 | $body = str_replace( |
||
499 | [ '$WATCHINGUSERNAME', |
||
500 | '$PAGEEDITDATE', |
||
501 | '$PAGEEDITTIME' ], |
||
502 | [ wfMessage( 'enotif_impersonal_salutation' )->inContentLanguage()->text(), |
||
503 | $wgContLang->date( $this->timestamp, false, false ), |
||
504 | $wgContLang->time( $this->timestamp, false, false ) ], |
||
505 | $this->body ); |
||
506 | |||
507 | return UserMailer::send( $addresses, $this->from, $this->subject, $body, [ |
||
508 | 'replyTo' => $this->replyto, |
||
509 | ] ); |
||
510 | } |
||
511 | |||
512 | } |
||
513 |
Only declaring a single property per statement allows you to later on add doc comments more easily.
It is also recommended by PSR2, so it is a common style that many people expect.