MediaWiki REL1_40
EmailUser.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Mail;
22
23use Config;
24use Hooks;
26use MailAddress;
31use MWException;
32use SpecialPage;
33use Status;
35use User;
36
43class EmailUser {
51 public static function getTarget( $target, User $sender ) {
52 if ( $target == '' ) {
53 wfDebug( "Target is empty." );
54
55 return 'notarget';
56 }
57
58 $nu = User::newFromName( $target );
59 $error = self::validateTarget( $nu, $sender );
60
61 return $error ?: $nu;
62 }
63
71 public static function validateTarget( $target, User $sender ) {
72 if ( !$target instanceof User || !$target->getId() ) {
73 wfDebug( "Target is invalid user." );
74
75 return 'notarget';
76 }
77
78 if ( !$target->isEmailConfirmed() ) {
79 wfDebug( "User has no valid email." );
80
81 return 'noemail';
82 }
83
84 if ( !$target->canReceiveEmail() ) {
85 wfDebug( "User does not allow user emails." );
86
87 return 'nowikiemail';
88 }
89
90 $userOptionsLookup = MediaWikiServices::getInstance()
91 ->getUserOptionsLookup();
92 if ( !$userOptionsLookup->getOption(
93 $target,
94 'email-allow-new-users'
95 ) && $sender->isNewbie()
96 ) {
97 wfDebug( "User does not allow user emails from new users." );
98
99 return 'nowikiemail';
100 }
101
102 $muteList = $userOptionsLookup->getOption(
103 $target,
104 'email-blacklist',
105 ''
106 );
107 if ( $muteList ) {
108 $muteList = MultiUsernameFilter::splitIds( $muteList );
110 ->getCentralIdLookup()
111 ->centralIdFromLocalUser( $sender );
112 if ( $senderId !== 0 && in_array( $senderId, $muteList ) ) {
113 wfDebug( "User does not allow user emails from this user." );
114
115 return 'nowikiemail';
116 }
117 }
118
119 return '';
120 }
121
131 public static function getPermissionsError( $user, $editToken, Config $config = null ) {
132 if ( $config === null ) {
133 wfDebug( __METHOD__ . ' called without a Config instance passed to it' );
134 $config = MediaWikiServices::getInstance()->getMainConfig();
135 }
136 if ( !$config->get( MainConfigNames::EnableEmail ) ||
137 !$config->get( MainConfigNames::EnableUserEmail ) ) {
138 return 'usermaildisabled';
139 }
140
141 // Run this before checking 'sendemail' permission
142 // to show appropriate message to anons (T160309)
143 if ( !$user->isEmailConfirmed() ) {
144 return 'mailnologin';
145 }
146
148 ->getPermissionManager()
149 ->userHasRight( $user, 'sendemail' )
150 ) {
151 return 'badaccess';
152 }
153
154 if ( $user->isBlockedFromEmailuser() ) {
155 wfDebug( "User is blocked from sending e-mail." );
156
157 return "blockedemailuser";
158 }
159
160 // Check the ping limiter without incrementing it - we'll check it
161 // again later and increment it on a successful send
162 if ( $user->pingLimiter( 'sendemail', 0 ) ) {
163 wfDebug( "Ping limiter triggered." );
164
165 return 'actionthrottledtext';
166 }
167
168 $hookErr = false;
169
170 Hooks::runner()->onUserCanSendEmail( $user, $hookErr );
171 Hooks::runner()->onEmailUserPermissionsErrors( $user, $editToken, $hookErr );
172
173 return $hookErr ?: null;
174 }
175
186 public static function submit( array $data, IContextSource $context ) {
187 $config = $context->getConfig();
188
189 $sender = $context->getUser();
190 $target = self::getTarget( $data['Target'], $sender );
191 if ( !$target instanceof User ) {
192 // Messages used here: notargettext, noemailtext, nowikiemailtext
193 return Status::newFatal( $target . 'text' );
194 }
195
196 $toAddress = MailAddress::newFromUser( $target );
197 $fromAddress = MailAddress::newFromUser( $sender );
198 $subject = $data['Subject'];
199 $text = $data['Text'];
200
201 // Add a standard footer and trim up trailing newlines
202 $text = rtrim( $text ) . "\n\n-- \n";
203 $text .= $context->msg(
204 'emailuserfooter',
205 $fromAddress->name,
206 $toAddress->name
207 )->inContentLanguage()->text();
208
209 if ( $config->get( MainConfigNames::EnableSpecialMute ) ) {
210 $specialMutePage = SpecialPage::getTitleFor( 'Mute', $sender->getName() );
211 $text .= "\n" . $context->msg(
212 'specialmute-email-footer',
213 $specialMutePage->getCanonicalURL(),
214 $sender->getName()
215 )->inContentLanguage()->text();
216 }
217
218 // Check and increment the rate limits
219 if ( $sender->pingLimiter( 'sendemail' ) ) {
220 throw new ThrottledError();
221 }
222
223 // Services that are needed, will be injected once this is moved to EmailUserUtils
224 // service, see T265541
225 $hookRunner = Hooks::runner();
226 $emailer = MediaWikiServices::getInstance()->getEmailer();
227
228 $error = false;
229 if ( !$hookRunner->onEmailUser( $toAddress, $fromAddress, $subject, $text, $error ) ) {
230 if ( $error instanceof Status ) {
231 return $error;
232 } elseif ( $error === false || $error === '' || $error === [] ) {
233 // Possibly to tell HTMLForm to pretend there was no submission?
234 return false;
235 } elseif ( $error === true ) {
236 // Hook sent the mail itself and indicates success?
237 return Status::newGood();
238 } elseif ( is_array( $error ) ) {
239 $status = Status::newGood();
240 foreach ( $error as $e ) {
241 $status->fatal( $e );
242 }
243 return $status;
244 } elseif ( $error instanceof MessageSpecifier ) {
245 return Status::newFatal( $error );
246 } else {
247 // Setting $error to something else was deprecated in 1.29 and
248 // removed in 1.36, and so an exception is now thrown
249 $type = is_object( $error ) ? get_class( $error ) : gettype( $error );
250 throw new MWException(
251 'EmailUser hook set $error to unsupported type ' . $type
252 );
253 }
254 }
255
256 if ( $config->get( MainConfigNames::UserEmailUseReplyTo ) ) {
265 $mailFrom = new MailAddress(
266 $config->get( MainConfigNames::PasswordSender ),
267 $context->msg( 'emailsender' )->inContentLanguage()->text()
268 );
269 $replyTo = $fromAddress;
270 } else {
286 $mailFrom = $fromAddress;
287 $replyTo = null;
288 }
289
290 $status = Status::wrap( $emailer->send(
291 $toAddress,
292 $mailFrom,
293 $subject,
294 $text,
295 null,
296 [ 'replyTo' => $replyTo ]
297 ) );
298
299 if ( !$status->isGood() ) {
300 return $status;
301 }
302
303 // if the user requested a copy of this mail, do this now,
304 // unless they are emailing themselves, in which case one
305 // copy of the message is sufficient.
306 if ( $data['CCMe'] && $toAddress != $fromAddress ) {
307 $ccTo = $fromAddress;
308 $ccFrom = $fromAddress;
309 $ccSubject = $context->msg( 'emailccsubject' )->plaintextParams(
310 $target->getName(),
311 $subject
312 )->text();
313 $ccText = $text;
314
315 $hookRunner->onEmailUserCC( $ccTo, $ccFrom, $ccSubject, $ccText );
316
317 if ( $config->get( MainConfigNames::UserEmailUseReplyTo ) ) {
318 $mailFrom = new MailAddress(
319 $config->get( MainConfigNames::PasswordSender ),
320 $context->msg( 'emailsender' )->inContentLanguage()->text()
321 );
322 $replyTo = $ccFrom;
323 } else {
324 $mailFrom = $ccFrom;
325 $replyTo = null;
326 }
327
328 $ccStatus = $emailer->send(
329 $ccTo,
330 $mailFrom,
331 $ccSubject,
332 $ccText,
333 null,
334 [ 'replyTo' => $replyTo ]
335 );
336 $status->merge( $ccStatus );
337 }
338
339 $hookRunner->onEmailUserComplete( $toAddress, $fromAddress, $subject, $text );
340
341 return $status;
342 }
343}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Hooks class.
Definition Hooks.php:38
MediaWiki exception.
Stores a single person's name and email address.
Command for sending emails to users.
Definition EmailUser.php:43
static submit(array $data, IContextSource $context)
Really send a mail.
static validateTarget( $target, User $sender)
Validate target User.
Definition EmailUser.php:71
static getPermissionsError( $user, $editToken, Config $config=null)
Check whether a user is allowed to send email.
static getTarget( $target, User $sender)
Validate target User.
Definition EmailUser.php:51
A class containing constants representing the names of configuration variables.
const EnableUserEmail
Name constant for the EnableUserEmail setting, for use with Config::get()
const EnableSpecialMute
Name constant for the EnableSpecialMute setting, for use with Config::get()
const EnableEmail
Name constant for the EnableEmail setting, for use with Config::get()
const PasswordSender
Name constant for the PasswordSender setting, for use with Config::get()
const UserEmailUseReplyTo
Name constant for the UserEmailUseReplyTo setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
static splitIds( $str)
Splits a newline separated list of user ids into an array.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:46
Show an error when the user hits a rate limit.
internal since 1.36
Definition User.php:71
static newFromName( $name, $validate='valid')
Definition User.php:592
isNewbie()
Determine whether the user is a newbie.
Definition User.php:2928
getId( $wikiId=self::LOCAL)
Get the user's ID.
Definition User.php:1647
Interface for configuration instances.
Definition Config.php:30
Interface for objects which can provide a MediaWiki context on request.
getConfig()
Get the site configuration.
msg( $key,... $params)
This is the method for getting translated interface messages.