36use UnexpectedValueException;
75 private string $editToken =
'';
102 $this->options = $options;
103 $this->hookRunner =
new HookRunner( $hookContainer );
104 $this->userOptionsLookup = $userOptionsLookup;
105 $this->centralIdLookup = $centralIdLookup;
106 $this->userFactory = $userFactory;
107 $this->emailer = $emailer;
108 $this->messageFormatterFactory = $messageFormatterFactory;
109 $this->contLangMsgFormatter = $contLangMsgFormatter;
111 $this->sender = $sender;
122 $targetIdentity = $target->getUser();
124 if ( !$targetIdentity->getId() ) {
125 return StatusValue::newFatal(
'emailnotarget' );
129 return StatusValue::newFatal(
'noemailtext' );
132 $targetUser = $this->userFactory->newFromUserIdentity( $targetIdentity );
133 if ( !$targetUser->canReceiveEmail() ) {
134 return StatusValue::newFatal(
'nowikiemailtext' );
137 $senderUser = $this->userFactory->newFromAuthority( $this->sender );
139 !$this->userOptionsLookup->getOption( $targetIdentity,
'email-allow-new-users' ) &&
140 $senderUser->isNewbie()
142 return StatusValue::newFatal(
'nowikiemailtext' );
145 $muteList = $this->userOptionsLookup->getOption(
152 $senderId = $this->centralIdLookup->centralIdFromLocalUser( $this->sender->getUser() );
153 if ( $senderId !== 0 && in_array( $senderId, $muteList ) ) {
172 return StatusValue::newFatal(
'usermaildisabled' );
175 $user = $this->userFactory->newFromAuthority( $this->sender );
179 if ( !$user->isEmailConfirmed() ) {
180 return StatusValue::newFatal(
'mailnologin' );
183 $status = PermissionStatus::newGood();
184 if ( !$this->sender->isDefinitelyAllowed(
'sendemail', $status ) ) {
191 $this->hookRunner->onUserCanSendEmail( $user, $hookErr );
192 $this->hookRunner->onEmailUserPermissionsErrors( $user, $this->editToken, $hookErr );
193 if ( is_array( $hookErr ) ) {
195 $msgParamsArray = is_array( $hookErr[2] ) ? $hookErr[2] : [];
196 $ret = StatusValue::newFatal( $hookErr[1], ...$msgParamsArray );
197 $ret->value = $hookErr[0];
211 $status = $this->canSend();
212 if ( !$status->isOK() ) {
216 $status = PermissionStatus::newGood();
217 if ( !$this->sender->authorizeAction(
'sendemail', $status ) ) {
221 $hookRes = $this->hookRunner->onEmailUserAuthorizeSend( $this->sender, $status );
222 if ( !$hookRes && !$status->isGood() ) {
226 return StatusValue::newGood();
246 $senderIdentity = $this->sender->getUser();
247 $targetStatus = $this->validateTarget( $target );
248 if ( !$targetStatus->isGood() ) {
249 return $targetStatus;
252 $senderUser = $this->userFactory->newFromAuthority( $this->sender );
254 $toAddress = MailAddress::newFromUser( $target );
255 $fromAddress = MailAddress::newFromUser( $senderUser );
258 $text = rtrim( $text ) .
"\n\n-- \n";
259 $text .= $this->contLangMsgFormatter->format(
260 MessageValue::new(
'emailuserfooter', [ $fromAddress->name, $toAddress->name ] )
263 if ( $this->options->get( MainConfigNames::EnableSpecialMute ) ) {
264 $text .=
"\n" . $this->contLangMsgFormatter->format(
266 'specialmute-email-footer',
268 $this->getSpecialMuteCanonicalURL( $senderIdentity->getName() ),
269 $senderIdentity->getName()
277 if ( !$this->hookRunner->onEmailUser( $toAddress, $fromAddress, $subject, $text, $error ) ) {
280 } elseif ( $error ===
false || $error ===
'' || $error === [] ) {
282 return StatusValue::newFatal(
'hookaborted' );
283 } elseif ( $error ===
true ) {
286 } elseif ( is_array( $error ) ) {
288 foreach ( $error as $e ) {
289 $status->fatal( $e );
292 } elseif ( $error instanceof MessageSpecifier ) {
297 $type = get_debug_type( $error );
298 throw new UnexpectedValueException(
299 'EmailUser hook set $error to unsupported type ' . $type
305 $hookRes = $this->hookRunner->onEmailUserSendEmail(
314 if ( !$hookRes && !$hookStatus->isGood() ) {
318 [ $mailFrom, $replyTo ] = $this->getFromAndReplyTo( $fromAddress );
320 $status = $this->emailer->send(
326 [
'replyTo' => $replyTo ]
329 if ( !$status->isGood() ) {
336 if ( $CCMe && !$toAddress->equals( $fromAddress ) ) {
337 $userMsgFormatter = $this->messageFormatterFactory->getTextFormatter( $langCode );
338 $ccTo = $fromAddress;
339 $ccFrom = $fromAddress;
340 $ccSubject = $userMsgFormatter->format(
341 MessageValue::new(
'emailccsubject' )->plaintextParams(
348 $this->hookRunner->onEmailUserCC( $ccTo, $ccFrom, $ccSubject, $ccText );
350 [ $mailFrom, $replyTo ] = $this->getFromAndReplyTo( $ccFrom );
352 $ccStatus = $this->emailer->send(
358 [
'replyTo' => $replyTo ]
360 $status->merge( $ccStatus );
363 $this->hookRunner->onEmailUserComplete( $toAddress, $fromAddress, $subject, $text );
373 private function getFromAndReplyTo(
MailAddress $fromAddress ): array {
374 if ( $this->options->get( MainConfigNames::UserEmailUseReplyTo ) ) {
384 $this->options->get( MainConfigNames::PasswordSender ),
385 $this->contLangMsgFormatter->format( MessageValue::new(
'emailsender' ) )
387 $replyTo = $fromAddress;
404 $mailFrom = $fromAddress;
407 return [ $mailFrom, $replyTo ];
416 private function getSpecialMuteCanonicalURL(
string $targetName ): string {
417 if ( defined(
'MW_PHPUNIT_TEST' ) ) {
418 return "Ceci n'est pas une URL";
420 return SpecialPage::getTitleFor(
'Mute', $targetName )->getCanonicalURL();
428 $this->editToken = $token;
if(!defined('MW_SETUP_CALLBACK'))
Represent and format a single name and email address pair for SMTP.
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.
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.