Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
79.41% |
27 / 34 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
SGHooks | |
79.41% |
27 / 34 |
|
0.00% |
0 / 2 |
14.47 | |
0.00% |
0 / 1 |
onAlternateUserMailer | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
7.01 | |||
sendEmail | |
68.42% |
13 / 19 |
|
0.00% |
0 / 1 |
7.13 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\SendGrid; |
4 | |
5 | use Exception; |
6 | use FormatJson; |
7 | use MailAddress; |
8 | use MediaWiki\Hook\AlternateUserMailerHook; |
9 | use MWException; |
10 | use RequestContext; |
11 | use SendGrid; |
12 | use SendGrid\Mail\Mail; |
13 | use SendGrid\Mail\TypeException; |
14 | use Status; |
15 | |
16 | /** |
17 | * Hooks for SendGrid extension for MediaWiki |
18 | * |
19 | * This program is free software; you can redistribute it and/or modify |
20 | * it under the terms of the GNU General Public License as published by |
21 | * the Free Software Foundation; either version 2 of the License, or |
22 | * (at your option) any later version. |
23 | * |
24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 | * GNU General Public License for more details. |
28 | * |
29 | * You should have received a copy of the GNU General Public License along |
30 | * with this program; if not, write to the Free Software Foundation, Inc., |
31 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
32 | * http://www.gnu.org/copyleft/gpl.html |
33 | * |
34 | * @file |
35 | * @author Derick Alangi <alangiderick@gmail.com> |
36 | * @license GPL-2.0-or-later |
37 | * |
38 | * @link https://www.mediawiki.org/wiki/Extension:SendGrid Documentation |
39 | * @ingroup Extensions |
40 | */ |
41 | class SGHooks implements AlternateUserMailerHook { |
42 | |
43 | /** |
44 | * @internal For use to get the SendGrid API key |
45 | * @var string |
46 | */ |
47 | public const SENDGRID_API_KEY = 'SendGridAPIKey'; |
48 | |
49 | private const RESPONSE_CODE_OK = 202; |
50 | |
51 | /** |
52 | * Hook handler to send e-mails |
53 | * |
54 | * @param array $headers |
55 | * @param array $to |
56 | * @param MailAddress $from |
57 | * @param string $subject |
58 | * @param string $body |
59 | * |
60 | * @return bool|void|string |
61 | * @throws MWException|TypeException |
62 | */ |
63 | public function onAlternateUserMailer( $headers, $to, $from, $subject, $body ) { |
64 | $conf = RequestContext::getMain()->getConfig(); |
65 | |
66 | $sendgridAPIKey = $conf->get( self::SENDGRID_API_KEY ); |
67 | |
68 | if ( $sendgridAPIKey === '' || !isset( $sendgridAPIKey ) ) { |
69 | throw new MWException( |
70 | 'Please update your LocalSettings.php with the correct SendGrid API key.' |
71 | ); |
72 | } |
73 | |
74 | $sendgrid = new SendGrid( $sendgridAPIKey ); |
75 | $response = $this->sendEmail( $to, $from, $subject, $body, $sendgrid ); |
76 | |
77 | if ( $response !== null && $response->isOK() ) { |
78 | // The email was successfully sent. Return |
79 | // `false` to skip calling `mail()` which |
80 | // is the regular way that core sends mails. |
81 | return false; |
82 | } |
83 | |
84 | if ( $response === null || !$response->isOK() ) { |
85 | // Inform the user why the email was not sent. |
86 | $error = FormatJson::decode( $response->getErrors()[0]['message'] ); |
87 | throw new MWException( |
88 | $error->errors[0]->message ?? '$wgPasswordSender does not match Sender Identity on SendGrid' |
89 | ); |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * Send Email via the API |
95 | * |
96 | * @param array $to |
97 | * @param MailAddress $from |
98 | * @param string $subject |
99 | * @param string $body |
100 | * @param SendGrid|null $sendgrid |
101 | * |
102 | * @return Status|null |
103 | * @throws MWException|TypeException |
104 | */ |
105 | public function sendEmail( |
106 | array $to, |
107 | MailAddress $from, |
108 | $subject, |
109 | $body, |
110 | ?SendGrid $sendgrid = null |
111 | ): ?Status { |
112 | if ( $sendgrid === null ) { |
113 | return null; |
114 | } |
115 | |
116 | // Get $to and $from email addresses from the |
117 | // `array` and `MailAddress` object respectively |
118 | $email = new Mail(); |
119 | $email->addTo( $to[0]->address ); |
120 | |
121 | if ( filter_var( $from->address, FILTER_VALIDATE_EMAIL ) ) { |
122 | try { |
123 | $email->setFrom( $from->address ); |
124 | } catch ( TypeException $e ) { |
125 | return Status::newGood( $e->getMessage() ); |
126 | } |
127 | } else { |
128 | throw new MWException( |
129 | 'Invalid "from" email, check the $wgPasswordSender configs' |
130 | ); |
131 | } |
132 | |
133 | $email->setSubject( $subject ); |
134 | $email->addContent( 'text/plain', $body ); |
135 | |
136 | try { |
137 | $response = $sendgrid->send( $email ); |
138 | if ( $response->statusCode() === self::RESPONSE_CODE_OK ) { |
139 | return Status::newGood(); |
140 | } |
141 | |
142 | return Status::newFatal( 'sendgrid-email-not-sent' ); |
143 | } catch ( Exception $e ) { |
144 | return Status::newGood( $e->getMessage() ); |
145 | } |
146 | } |
147 | |
148 | } |