Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 78 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
EchoHtmlDigestEmailFormatter | |
0.00% |
0 / 78 |
|
0.00% |
0 / 9 |
156 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
formatModels | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
2 | |||
renderBody | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
getCategoryTitle | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
groupByCategory | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
applyStyleToCategory | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
applyStyleToEvent | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
renderDigestList | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
renderAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Notifications\Formatters; |
4 | |
5 | use Language; |
6 | use MediaWiki\Html\Html; |
7 | use MediaWiki\Parser\Sanitizer; |
8 | use MediaWiki\SpecialPage\SpecialPage; |
9 | use MediaWiki\User\User; |
10 | |
11 | class EchoHtmlDigestEmailFormatter extends EchoEventDigestFormatter { |
12 | |
13 | /** |
14 | * @var string 'daily' or 'weekly' |
15 | */ |
16 | protected $digestMode; |
17 | |
18 | public function __construct( User $user, Language $language, $digestMode ) { |
19 | parent::__construct( $user, $language ); |
20 | $this->digestMode = $digestMode; |
21 | } |
22 | |
23 | /** |
24 | * @param EchoEventPresentationModel[] $models |
25 | * @return string[] Array of the following format: |
26 | * [ 'body' => formatted email body, |
27 | * 'subject' => formatted email subject ] |
28 | */ |
29 | protected function formatModels( array $models ) { |
30 | // echo-email-batch-body-intro-daily |
31 | // echo-email-batch-body-intro-weekly |
32 | $intro = $this->msg( 'echo-email-batch-body-intro-' . $this->digestMode ) |
33 | ->params( $this->user->getName() ) |
34 | ->parse(); |
35 | $intro = nl2br( $intro ); |
36 | |
37 | $eventsByCategory = $this->groupByCategory( $models ); |
38 | ksort( $eventsByCategory ); |
39 | $digestList = $this->renderDigestList( $eventsByCategory ); |
40 | |
41 | $htmlFormatter = new EchoHtmlEmailFormatter( $this->user, $this->language ); |
42 | |
43 | $body = $this->renderBody( |
44 | $this->language, |
45 | $intro, |
46 | $digestList, |
47 | $this->renderAction(), |
48 | $htmlFormatter->getFooter() |
49 | ); |
50 | |
51 | // echo-email-batch-subject-daily |
52 | // echo-email-batch-subject-weekly |
53 | $subject = $this->msg( 'echo-email-batch-subject-' . $this->digestMode ) |
54 | ->numParams( count( $models ), count( $models ) ) |
55 | ->text(); |
56 | |
57 | return [ |
58 | 'subject' => $subject, |
59 | 'body' => $body, |
60 | ]; |
61 | } |
62 | |
63 | private function renderBody( Language $language, $intro, $digestList, $action, $footer ) { |
64 | $alignStart = $language->alignStart(); |
65 | $langCode = $language->getHtmlCode(); |
66 | $langDir = $language->getDir(); |
67 | |
68 | // phpcs:disable Generic.Files.LineLength |
69 | return <<< EOF |
70 | <html><head> |
71 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
72 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
73 | <style> |
74 | @media only screen and (max-width: 480px){ |
75 | table[id="email-container"]{max-width:600px !important; width:100% !important;} |
76 | } |
77 | </style> |
78 | </head><body> |
79 | <table cellspacing="0" cellpadding="0" border="0" width="100%" align="center" lang="$langCode" dir="$langDir"> |
80 | <tr> |
81 | <td bgcolor="#EAECF0"><center> |
82 | <br /><br /> |
83 | <table cellspacing="0" cellpadding="0" border="0" width="600" id="email-container"> |
84 | <tr> |
85 | <td bgcolor="#FFFFFF" width="5%"> </td> |
86 | <td bgcolor="#FFFFFF" width="6%"> </td> |
87 | <td bgcolor="#FFFFFF" width="79%" style="line-height:40px;"> </td> |
88 | <td bgcolor="#FFFFFF" width="10%"> </td> |
89 | </tr> |
90 | <tr> |
91 | <td bgcolor="#FFFFFF" rowspan="2"> </td> |
92 | <td bgcolor="#FFFFFF" rowspan="2"> </td> |
93 | <td bgcolor="#FFFFFF" align="center" style="font-family: Arial, Helvetica, sans-serif; font-size:13px; line-height:20px; color:#72777D; text-align: center;">$intro</td> |
94 | <td bgcolor="#FFFFFF" rowspan="2"> </td> |
95 | </tr> |
96 | <tr> |
97 | <td bgcolor="#FFFFFF" align="$alignStart" style="font-family: Arial, Helvetica, sans-serif; line-height: 20px; font-weight: 600;"> |
98 | <table cellspacing="0" cellpadding="0" border="0" width="100%"> |
99 | <tr> |
100 | <td bgcolor="#FFFFFF" align="$alignStart" style="font-family: Arial, Helvetica, sans-serif; font-size:13px; color: #54595D; padding-top: 25px;"> |
101 | $digestList |
102 | </td> |
103 | </tr> |
104 | </table> |
105 | <br /><br /> |
106 | </td> |
107 | </tr> |
108 | <tr> |
109 | <td bgcolor="#FFFFFF"> </td> |
110 | <td bgcolor="#FFFFFF"> </td> |
111 | <td bgcolor="#FFFFFF" style="line-height:60px;" align="center">$action</td> |
112 | <td bgcolor="#FFFFFF"> </td> |
113 | </tr> |
114 | <tr> |
115 | <td bgcolor="#FFFFFF"> </td> |
116 | <td bgcolor="#FFFFFF"> </td> |
117 | <td bgcolor="#FFFFFF" style="line-height:40px;"> </td> |
118 | <td bgcolor="#FFFFFF"> </td> |
119 | </tr> |
120 | <tr> |
121 | <td> </td> |
122 | <td> </td> |
123 | <td align="$alignStart" style="font-family: Arial, Helvetica, sans-serif; font-size:10px; line-height:13px; color:#72777D; padding: 10px 20px;"><br /> |
124 | $footer |
125 | <br /><br /> |
126 | </td> |
127 | <td> </td> |
128 | </tr> |
129 | <tr> |
130 | <td colspan="4"> </td> |
131 | </tr> |
132 | </table> |
133 | <br><br></center> |
134 | </td> |
135 | </tr> |
136 | </table> |
137 | </body></html> |
138 | EOF; |
139 | // phpcs:enable Generic.Files.LineLength |
140 | } |
141 | |
142 | /** |
143 | * @param string $type Notification type |
144 | * @param int $count Number of notifications in this type's section |
145 | * @return string Formatted category section title |
146 | */ |
147 | private function getCategoryTitle( $type, $count ) { |
148 | return $this->msg( "echo-category-title-$type" ) |
149 | ->numParams( $count ) |
150 | ->parse(); |
151 | } |
152 | |
153 | /** |
154 | * @param EchoEventPresentationModel[] $models |
155 | * @return array [ 'category name' => EchoEventPresentationModel[] ] |
156 | */ |
157 | private function groupByCategory( array $models ) { |
158 | $eventsByCategory = []; |
159 | foreach ( $models as $model ) { |
160 | $eventsByCategory[$model->getCategory()][] = $model; |
161 | } |
162 | return $eventsByCategory; |
163 | } |
164 | |
165 | /** |
166 | * Apply style to notification category header |
167 | * @param string $category Can contain HTML. Is included as-is in HTML template, is not escaped. |
168 | * @return string |
169 | */ |
170 | protected function applyStyleToCategory( $category ) { |
171 | return <<< EOF |
172 | <tr> |
173 | <td colspan="2" style="color: #72777D; font-weight: normal; font-size: 13px; padding-top: 15px;"> |
174 | $category <br /> |
175 | <hr style="background-color:#FFFFFF; color:#FFFFFF; border: 1px solid #EAECF0;" /> |
176 | </td> |
177 | </tr> |
178 | EOF; |
179 | } |
180 | |
181 | /** |
182 | * Apply style to individual notification event |
183 | * @param EchoEventPresentationModel $model |
184 | * @return string |
185 | */ |
186 | protected function applyStyleToEvent( EchoEventPresentationModel $model ) { |
187 | $iconUrl = wfExpandUrl( |
188 | EchoIcon::getRasterizedUrl( $model->getIconType(), $this->language->getCode() ), |
189 | PROTO_CANONICAL |
190 | ); |
191 | |
192 | $imgSrc = Sanitizer::encodeAttribute( $iconUrl ); |
193 | |
194 | // notification text |
195 | $text = $model->getHeaderMessage()->parse(); |
196 | |
197 | return <<< EOF |
198 | <tr> |
199 | <td width="30"> |
200 | <img src="$imgSrc" width="30" height="30" style="vertical-align:middle;"> |
201 | </td> |
202 | <td style="font-family: Arial, Helvetica, sans-serif; font-size:13px; color: #54595D;"> |
203 | $text |
204 | </td> |
205 | </tr> |
206 | EOF; |
207 | } |
208 | |
209 | private function renderDigestList( $eventsByCategory ) { |
210 | $result = []; |
211 | // build the html section for each category |
212 | foreach ( $eventsByCategory as $category => $models ) { |
213 | $output = $this->applyStyleToCategory( |
214 | $this->getCategoryTitle( $category, count( $models ) ) |
215 | ); |
216 | foreach ( $models as $model ) { |
217 | $output .= "\n" . $this->applyStyleToEvent( $model ); |
218 | } |
219 | $result[] = '<table border="0" width="100%">' . $output . '</table>'; |
220 | } |
221 | |
222 | return trim( implode( "\n", $result ) ); |
223 | } |
224 | |
225 | private function renderAction() { |
226 | return Html::element( |
227 | 'a', |
228 | [ |
229 | 'href' => SpecialPage::getTitleFor( 'Notifications' ) |
230 | ->getFullURL( '', false, PROTO_CANONICAL ), |
231 | 'style' => EchoHtmlEmailFormatter::PRIMARY_LINK_STYLE, |
232 | ], |
233 | $this->msg( 'echo-email-batch-link-text-view-all-notifications' )->text() |
234 | ); |
235 | } |
236 | |
237 | } |