Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 120 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
TranslationNotifyUser | |
0.00% |
0 / 120 |
|
0.00% |
0 / 8 |
210 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
leaveUserMessage | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
2 | |||
sendTranslationNotificationEmail | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
2 | |||
getRelevantLanguages | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
20 | |||
getUserLanguageOption | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getUserFirstLanguage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUserLanguages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
getUrlProtocol | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /* |
3 | * @file |
4 | * @license GPL-2.0-or-later |
5 | */ |
6 | |
7 | namespace MediaWiki\Extension\TranslationNotifications\Utilities; |
8 | |
9 | use MediaWiki\Extension\TranslationNotifications\Jobs\TranslationNotificationsEmailJob; |
10 | use MediaWiki\MassMessage\Job\MassMessageJob; |
11 | use MediaWiki\MediaWikiServices; |
12 | use MediaWiki\SpecialPage\SpecialPage; |
13 | use MediaWiki\Title\Title; |
14 | use MediaWiki\User\User; |
15 | use MediaWiki\User\UserIdentity; |
16 | use MediaWiki\WikiMap\WikiMap; |
17 | use Message; |
18 | |
19 | /** |
20 | * Encapsulates the logic needed to create a notification to be sent to Users based on the |
21 | * type of notification they want. Creates the necessary job classes that are then used to |
22 | * actually deliver the notification. |
23 | * @since 2019.10 |
24 | */ |
25 | class TranslationNotifyUser { |
26 | private Title $translatablePageTitle; |
27 | private User $notifier; |
28 | private string $noReplyAddress; |
29 | /** @var string[] */ |
30 | private array $localInterwikis; |
31 | private bool $httpsInEmail; |
32 | |
33 | // Request information |
34 | /** Request priority: `unset`, `high`, `medium` or `low` */ |
35 | private string $priority; |
36 | private string $deadline; |
37 | private string $notificationText; |
38 | /** |
39 | * A list of languages for which the translators are to be notified. Empty for all languages. |
40 | * @var string[] |
41 | */ |
42 | private array $languagesToNotify; |
43 | |
44 | /** |
45 | * @param Title $translatablePageTitle |
46 | * @param User $notifier |
47 | * @param string[] $localInterwikis |
48 | * @param string $noReplyAddress |
49 | * @param bool $httpsInEmail |
50 | * @param array $requestData |
51 | */ |
52 | public function __construct( |
53 | Title $translatablePageTitle, User $notifier, $localInterwikis, $noReplyAddress, |
54 | $httpsInEmail, $requestData |
55 | ) { |
56 | $this->notifier = $notifier; |
57 | $this->translatablePageTitle = $translatablePageTitle; |
58 | |
59 | $this->noReplyAddress = $noReplyAddress; |
60 | $this->localInterwikis = $localInterwikis; |
61 | $this->httpsInEmail = $httpsInEmail; |
62 | |
63 | $this->notificationText = $requestData['text']; |
64 | $this->languagesToNotify = $requestData['languagesToNotify']; |
65 | $this->priority = $requestData['priority'] ?? ''; |
66 | $this->deadline = $requestData['deadline'] ?? ''; |
67 | } |
68 | |
69 | /** |
70 | * Leave a message on the user's talk page. |
71 | * @param User $translator To whom the message to be sent |
72 | * @param string $destination Whether to send it to a talk page on this wiki |
73 | * ('talkpageHere', default) or another one ('talkpageInOtherWiki'). |
74 | * @return MassMessageJob |
75 | */ |
76 | public function leaveUserMessage( |
77 | User $translator, |
78 | $destination = 'talkpageHere' |
79 | ) { |
80 | $relevantLanguages = $this->getRelevantLanguages( $translator, $this->languagesToNotify ); |
81 | $userFirstLanguageCode = $this->getUserFirstLanguage( $translator ); |
82 | $userFirstLanguage = MediaWikiServices::getInstance()->getLanguageFactory() |
83 | ->getLanguage( $userFirstLanguageCode ); |
84 | |
85 | $text = wfMessage( |
86 | 'translationnotifications-talkpage-body', |
87 | $translator->getName(), |
88 | NotificationMessageBuilder::getUserName( $translator ), |
89 | $userFirstLanguage->listToText( array_values( $relevantLanguages ) ), |
90 | NotificationMessageBuilder::getMessageTitle( |
91 | $this->translatablePageTitle, $destination, $this->localInterwikis |
92 | ), |
93 | NotificationMessageBuilder::getTranslationURLs( |
94 | $this->translatablePageTitle, $relevantLanguages, 'talkpage', |
95 | $userFirstLanguage, $this->getUrlProtocol() |
96 | ), |
97 | NotificationMessageBuilder::getPriorityClause( $userFirstLanguage, $this->priority ), |
98 | NotificationMessageBuilder::getDeadlineClause( $userFirstLanguage, $this->deadline ), |
99 | NotificationMessageBuilder::getNotificationMessage( |
100 | MediaWikiServices::getInstance()->getContentLanguage(), |
101 | $this->notificationText |
102 | ) |
103 | )->numParams( count( $relevantLanguages ) ) // $9 |
104 | ->params( NotificationMessageBuilder::getSignupURL( $this->getUrlProtocol() ) ) // $10 |
105 | ->inLanguage( $userFirstLanguage ) |
106 | ->text(); |
107 | |
108 | // Bidi-isolation of site name from date |
109 | $text .= $userFirstLanguage->getDirMarkEntity() . |
110 | ', ~~~~~'; // Date and time |
111 | |
112 | // Note: Maybe this was originally meant for edit summary, but it's actually used as subject |
113 | $subject = wfMessage( |
114 | 'translationnotifications-edit-summary', |
115 | $this->translatablePageTitle |
116 | )->inLanguage( $userFirstLanguage )->text(); |
117 | |
118 | $listUrl = SpecialPage::getTitleFor( 'NotifyTranslators' )->getCanonicalURL(); |
119 | |
120 | $params = [ |
121 | // This is not the edit summary, but rather hidden comment left after the message |
122 | 'comment' => [ |
123 | $this->notifier->getName(), |
124 | WikiMap::getCurrentWikiId(), |
125 | $listUrl |
126 | ], |
127 | 'message' => $text, |
128 | 'subject' => $subject, |
129 | // Use canonical version of the namespace that works in all wikis and assume that |
130 | // user names are global across wikis |
131 | 'title' => 'User_talk:' . $translator->getName(), |
132 | ]; |
133 | |
134 | // Ignored, the page to deliver to is read from $params['title'] |
135 | $jobTitle = $translator->getTalkPage(); |
136 | |
137 | return new MassMessageJob( $jobTitle, $params ); |
138 | } |
139 | |
140 | /** |
141 | * Notify a user by email. |
142 | * @param User $translator User to whom the email is being sent |
143 | * @return TranslationNotificationsEmailJob |
144 | */ |
145 | public function sendTranslationNotificationEmail( |
146 | User $translator |
147 | ): TranslationNotificationsEmailJob { |
148 | $relevantLanguages = $this->getRelevantLanguages( $translator, $this->languagesToNotify ); |
149 | $userFirstLanguage = MediaWikiServices::getInstance()->getLanguageFactory() |
150 | ->getLanguage( $this->getUserFirstLanguage( $translator ) ); |
151 | $emailSubject = NotificationMessageBuilder::getNotificationSubject( |
152 | $this->translatablePageTitle, $userFirstLanguage |
153 | ); |
154 | |
155 | $translationUrls = NotificationMessageBuilder::getTranslationURLs( |
156 | $this->translatablePageTitle, |
157 | $relevantLanguages, |
158 | 'email', |
159 | $userFirstLanguage, |
160 | $this->getUrlProtocol() |
161 | ); |
162 | |
163 | $emailBody = wfMessage( 'translationnotifications-email-body' ) |
164 | ->params( |
165 | NotificationMessageBuilder::getUserName( $translator ), // $1 |
166 | $userFirstLanguage->listToText( array_values( $relevantLanguages ) ), |
167 | $this->translatablePageTitle, |
168 | $translationUrls, // $4 |
169 | NotificationMessageBuilder::getPriorityClause( $userFirstLanguage, $this->priority ), |
170 | NotificationMessageBuilder::getDeadlineClause( $userFirstLanguage, $this->deadline ), |
171 | $this->notificationText, // $7 |
172 | NotificationMessageBuilder::getSignupURL( $this->getUrlProtocol() ), |
173 | Message::numParam( count( $relevantLanguages ) ), |
174 | $translator->getName() // $10 |
175 | )->inLanguage( $userFirstLanguage )->text(); |
176 | |
177 | $sender = $this->notifier; |
178 | |
179 | // Do not publish the sender's email, but include his/her name |
180 | $emailFrom = TranslationNotificationsEmailJob::buildAddress( |
181 | $this->noReplyAddress, |
182 | $sender->getName(), |
183 | $sender->getRealName() |
184 | ); |
185 | |
186 | $params = [ |
187 | 'to' => TranslationNotificationsEmailJob::addressFromUser( $translator ), |
188 | 'from' => $emailFrom, |
189 | 'body' => $emailBody, |
190 | 'subject' => $emailSubject, |
191 | 'replyTo' => $emailFrom, |
192 | ]; |
193 | |
194 | return new TranslationNotificationsEmailJob( $this->translatablePageTitle, $params ); |
195 | } |
196 | |
197 | /** |
198 | * Returns a list of language codes and names for the current |
199 | * notification to the user. |
200 | * @param User $user User to whom the email is being sent |
201 | * @param string[] $languagesToNotify A list of languages that are notified. |
202 | * Empty for all languages. |
203 | * @return string[] Array of language codes |
204 | */ |
205 | protected function getRelevantLanguages( User $user, $languagesToNotify ) { |
206 | $userLanguages = $this->getUserLanguages( $user ); |
207 | $userFirstLanguageCode = $userLanguages[0]; |
208 | $limitLanguages = count( $languagesToNotify ); |
209 | $userLanguageNames = []; |
210 | |
211 | $languageNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils(); |
212 | foreach ( $userLanguages as $langCode ) { |
213 | // Don't add this language if particular languages were |
214 | // specified and this language was not one of them. |
215 | if ( ( $limitLanguages && !in_array( $langCode, $languagesToNotify ) ) ) { |
216 | continue; |
217 | } |
218 | |
219 | $userLanguageNames[$langCode] = $languageNameUtils->getLanguageName( |
220 | $langCode, |
221 | $userFirstLanguageCode |
222 | ); |
223 | } |
224 | |
225 | return $userLanguageNames; |
226 | } |
227 | |
228 | /** |
229 | * Returns a language that a user signed up for in |
230 | * Special:TranslatorSignup. |
231 | * @param UserIdentity $user |
232 | * @param int $langNum Number of language. |
233 | * @return string Language code, or null if it wasn't defined. |
234 | */ |
235 | protected function getUserLanguageOption( UserIdentity $user, $langNum ) { |
236 | return MediaWikiServices::getInstance() |
237 | ->getUserOptionsLookup() |
238 | ->getOption( $user, "translationnotifications-lang-$langNum" ); |
239 | } |
240 | |
241 | /** |
242 | * Returns the code of the first language to which a user signed up in |
243 | * Special:TranslatorSignup. |
244 | * @param User $user |
245 | * @return string Language code. |
246 | */ |
247 | protected function getUserFirstLanguage( User $user ) { |
248 | return $this->getUserLanguageOption( $user, 1 ); |
249 | } |
250 | |
251 | /** |
252 | * Returns an array of all language codes to which a user signed up in |
253 | * Special:TranslatorSignup. |
254 | * @param User $user |
255 | * @return array of language codes. |
256 | */ |
257 | protected function getUserLanguages( User $user ) { |
258 | $userLanguages = []; |
259 | |
260 | foreach ( range( 1, 3 ) as $langNum ) { |
261 | $nextLanguage = $this->getUserLanguageOption( $user, $langNum ); |
262 | if ( $nextLanguage !== '' ) { |
263 | $userLanguages[] = $nextLanguage; |
264 | } |
265 | } |
266 | |
267 | return $userLanguages; |
268 | } |
269 | |
270 | /** |
271 | * Returns the URL protocol to be used based on configuration |
272 | * @return string|int a PROTO_* constant |
273 | */ |
274 | protected function getUrlProtocol() { |
275 | return !$this->httpsInEmail |
276 | ? PROTO_CANONICAL |
277 | : PROTO_HTTPS; |
278 | } |
279 | } |