23 use BadMethodCallException;
45 use UnexpectedValueException;
111 $this->options = $options;
112 $this->hookRunner =
new HookRunner( $hookContainer );
113 $this->userOptionsLookup = $userOptionsLookup;
114 $this->centralIdLookup = $centralIdLookup;
115 $this->userFactory = $userFactory;
116 $this->emailer = $emailer;
117 $this->messageFormatterFactory = $messageFormatterFactory;
118 $this->contLangMsgFormatter = $contLangMsgFormatter;
120 $this->sender = $sender;
131 $targetIdentity = $target->
getUser();
133 if ( !$targetIdentity->getId() ) {
141 $targetUser = $this->userFactory->newFromUserIdentity( $targetIdentity );
142 if ( !$targetUser->canReceiveEmail() ) {
146 $senderUser = $this->userFactory->newFromAuthority( $this->sender );
148 !$this->userOptionsLookup->getOption( $targetIdentity,
'email-allow-new-users' ) &&
149 $senderUser->isNewbie()
154 $muteList = $this->userOptionsLookup->getOption(
161 $senderId = $this->centralIdLookup->centralIdFromLocalUser( $this->sender->getUser() );
162 if ( $senderId !== 0 && in_array( $senderId, $muteList ) ) {
188 $user = $this->userFactory->newFromAuthority( $this->sender );
192 if ( !$user->isEmailConfirmed() ) {
198 if ( !$this->sender->isAllowed(
'sendemail' ) ) {
202 $block = $this->sender->getBlock();
203 if ( $block instanceof AbstractBlock && $block->appliesToRight(
'sendemail' ) ) {
209 if ( $user->pingLimiter(
'sendemail', 0 ) ) {
216 $this->hookRunner->onUserCanSendEmail( $user, $hookErr );
217 $this->hookRunner->onEmailUserPermissionsErrors( $user, $editToken, $hookErr );
218 if ( is_array( $hookErr ) ) {
220 $msgParamsArray = is_array( $hookErr[2] ) ? $hookErr[2] : [];
222 $ret->value = $hookErr[0];
226 $hookRes = $this->hookRunner->onEmailUserAuthorizeSend( $this->sender, $hookStatus );
227 if ( !$hookRes && !$hookStatus->isGood() ) {
251 $senderIdentity = $this->sender->
getUser();
252 $targetStatus = $this->validateTarget( $target );
253 if ( !$targetStatus->isGood() ) {
254 return $targetStatus;
257 $senderUser = $this->userFactory->newFromAuthority( $this->sender );
259 if ( $senderUser->pingLimiter(
'sendemail' ) ) {
260 throw $this->getThrottledError();
267 $text = rtrim( $text ) .
"\n\n-- \n";
268 $text .= $this->contLangMsgFormatter->format(
269 MessageValue::new(
'emailuserfooter', [ $fromAddress->name, $toAddress->name ] )
272 if ( $this->options->get( MainConfigNames::EnableSpecialMute ) ) {
273 $text .=
"\n" . $this->contLangMsgFormatter->format(
275 'specialmute-email-footer',
277 $this->getSpecialMuteCanonicalURL( $senderIdentity->getName() ),
278 $senderIdentity->getName()
286 if ( !$this->hookRunner->onEmailUser( $toAddress, $fromAddress, $subject, $text, $error ) ) {
289 } elseif ( $error ===
false || $error ===
'' || $error === [] ) {
292 } elseif ( $error ===
true ) {
295 } elseif ( is_array( $error ) ) {
297 foreach ( $error as $e ) {
298 $status->fatal( $e );
306 $type = is_object( $error ) ? get_class( $error ) : gettype( $error );
307 throw new UnexpectedValueException(
308 'EmailUser hook set $error to unsupported type ' . $type
314 $hookRes = $this->hookRunner->onEmailUserSendEmail(
323 if ( !$hookRes && !$hookStatus->isGood() ) {
327 [ $mailFrom, $replyTo ] = $this->getFromAndReplyTo( $fromAddress );
329 $status = $this->emailer->send(
335 [
'replyTo' => $replyTo ]
338 if ( !$status->isGood() ) {
345 if ( $CCMe && !$toAddress->equals( $fromAddress ) ) {
346 $userMsgFormatter = $this->messageFormatterFactory->getTextFormatter( $langCode );
347 $ccTo = $fromAddress;
348 $ccFrom = $fromAddress;
349 $ccSubject = $userMsgFormatter->format(
350 MessageValue::new(
'emailccsubject' )->plaintextParams(
357 $this->hookRunner->onEmailUserCC( $ccTo, $ccFrom, $ccSubject, $ccText );
359 [ $mailFrom, $replyTo ] = $this->getFromAndReplyTo( $ccFrom );
361 $ccStatus = $this->emailer->send(
367 [
'replyTo' => $replyTo ]
369 $status->merge( $ccStatus );
372 $this->hookRunner->onEmailUserComplete( $toAddress, $fromAddress, $subject, $text );
382 private function getFromAndReplyTo(
MailAddress $fromAddress ): array {
383 if ( $this->options->get( MainConfigNames::UserEmailUseReplyTo ) ) {
393 $this->options->get( MainConfigNames::PasswordSender ),
394 $this->contLangMsgFormatter->format( MessageValue::new(
'emailsender' ) )
396 $replyTo = $fromAddress;
413 $mailFrom = $fromAddress;
416 return [ $mailFrom, $replyTo ];
425 private function getSpecialMuteCanonicalURL(
string $targetName ): string {
426 if ( defined(
'MW_PHPUNIT_TEST' ) ) {
427 return "Ceci n'est pas une URL";
429 return SpecialPage::getTitleFor(
'Mute', $targetName )->getCanonicalURL();
438 private function getThrottledError() {
439 if ( defined(
'MW_PHPUNIT_TEST' ) ) {
440 return new RuntimeException(
"You are throttled, and I am not running heavy logic in the constructor" );
452 private function getBlockedMessage( User $user ):
Message {
453 if ( defined(
'MW_PHPUNIT_TEST' ) ) {
454 return new RawMessage(
'You shall not send' );
456 $blockErrorFormatter = MediaWikiServices::getInstance()->getBlockErrorFormatter();
457 $block = $user->getBlock();
459 throw new BadMethodCallException(
'This method should only be called if the user is blocked' );
461 return $blockErrorFormatter->getMessage(
if(!defined('MW_SETUP_CALLBACK'))
Stores a single person's name and email address.
static newFromUser(UserEmailContact $user)
Create a new MailAddress object for the given user.
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()
Parent class for all special pages.
The Message class deals with fetching and processing of interface message into a variety of formats.
Group all the pieces relevant to the context of a request into one instance.
static getMain()
Get the RequestContext object associated with the main request.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
Show an error when the user hits a rate limit.