Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
91.30% |
42 / 46 |
|
87.50% |
7 / 8 |
CRAP | |
0.00% |
0 / 1 |
MessageBuilder | |
91.30% |
42 / 46 |
|
87.50% |
7 / 8 |
19.24 | |
0.00% |
0 / 1 |
stripTildes | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
buildMessage | |
63.64% |
7 / 11 |
|
0.00% |
0 / 1 |
4.77 | |||
buildSubject | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
buildPlaintextSubject | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
sanitizeSubject | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
wrapBasedOnLanguage | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
needsWrapping | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
wrapContentWithLanguageAttributes | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\MassMessage; |
5 | |
6 | use MediaWiki\Html\Html; |
7 | use MediaWiki\Language\Language; |
8 | |
9 | /** |
10 | * Contains logic to build the message and subject to be posted |
11 | * @author Abijeet Patro |
12 | * @since 2022.01 |
13 | * @license GPL-2.0-or-later |
14 | */ |
15 | class MessageBuilder { |
16 | private const USE_INLINE = true; |
17 | |
18 | /** |
19 | * Strip tildes at the end of the message |
20 | * |
21 | * @param string $customMessage |
22 | * @return string |
23 | */ |
24 | public function stripTildes( string $customMessage ): string { |
25 | $strippedText = rtrim( $customMessage ); |
26 | |
27 | if ( $strippedText |
28 | && substr( $strippedText, -4 ) === '~~~~' |
29 | && substr( $strippedText, -5 ) !== '~~~~~' |
30 | ) { |
31 | $strippedText = substr( $strippedText, 0, -4 ); |
32 | } |
33 | |
34 | return $strippedText; |
35 | } |
36 | |
37 | /** |
38 | * Merge page message passed as message, wrap it in necessary HTML tags / attributes and |
39 | * adds language tagging if necessary. Includes a comment about who is the sender. |
40 | * |
41 | * @param string $customMessageText |
42 | * @param LanguageAwareText|null $pageContent |
43 | * @param Language|null $targetLanguage |
44 | * @param string[] $commentParams |
45 | * @return string |
46 | */ |
47 | public function buildMessage( |
48 | string $customMessageText, |
49 | ?LanguageAwareText $pageContent, |
50 | ?Language $targetLanguage, |
51 | array $commentParams |
52 | ): string { |
53 | $trimmedText = rtrim( $customMessageText ); |
54 | $fullMessageText = ''; |
55 | |
56 | if ( $pageContent ) { |
57 | $fullMessageText = $this->wrapBasedOnLanguage( $pageContent, $targetLanguage, !self::USE_INLINE ); |
58 | } |
59 | |
60 | // If either is empty, the extra new lines will be trimmed |
61 | $fullMessageText = trim( $fullMessageText . "\n\n" . $trimmedText ); |
62 | |
63 | // $commentParams will always be present unless we are runnning tests. |
64 | if ( $commentParams ) { |
65 | $commentMessage = wfMessage( 'massmessage-hidden-comment' )->params( $commentParams ); |
66 | if ( $targetLanguage ) { |
67 | $commentMessage = $commentMessage->inLanguage( $targetLanguage ); |
68 | } |
69 | $fullMessageText .= "\n" . $commentMessage->text(); |
70 | } |
71 | |
72 | return $fullMessageText; |
73 | } |
74 | |
75 | /** |
76 | * Compose the subject depending on page subject or subject and target language. |
77 | * |
78 | * @param string $customSubject |
79 | * @param LanguageAwareText|null $pageSubject |
80 | * @param Language|null $targetPageLanguage |
81 | * @return string |
82 | */ |
83 | public function buildSubject( |
84 | string $customSubject, |
85 | ?LanguageAwareText $pageSubject, |
86 | ?Language $targetPageLanguage |
87 | ): string { |
88 | if ( $pageSubject ) { |
89 | $strippedPageSubject = new LanguageAwareText( |
90 | $this->sanitizeSubject( $pageSubject->getWikitext() ), |
91 | $pageSubject->getLanguageCode(), |
92 | $pageSubject->getLanguageDirection() |
93 | ); |
94 | |
95 | return $this->wrapBasedOnLanguage( $strippedPageSubject, $targetPageLanguage, self::USE_INLINE ); |
96 | } |
97 | |
98 | return $this->sanitizeSubject( $customSubject ); |
99 | } |
100 | |
101 | /** |
102 | * Compose the page subject without any HTML wrapping |
103 | * |
104 | * @param string $customSubject |
105 | * @param LanguageAwareText|null $pageSubject |
106 | * @return string |
107 | */ |
108 | public function buildPlaintextSubject( string $customSubject, ?LanguageAwareText $pageSubject ): string { |
109 | if ( $pageSubject ) { |
110 | return $this->sanitizeSubject( $pageSubject->getWikitext() ); |
111 | } |
112 | |
113 | return $this->sanitizeSubject( $customSubject ); |
114 | } |
115 | |
116 | /** |
117 | * Remove all newlines in-between content and remove tags |
118 | * |
119 | * @param string $subject |
120 | * @return string |
121 | */ |
122 | private function sanitizeSubject( string $subject ): string { |
123 | return rtrim( strip_tags( str_replace( "\n", '', $subject ) ) ); |
124 | } |
125 | |
126 | /** |
127 | * Wraps the page content based on the page content and the target page language |
128 | * |
129 | * @param LanguageAwareText $pageContent |
130 | * @param Language|null $targetLanguage |
131 | * @param bool $useInline |
132 | * @return string |
133 | */ |
134 | private function wrapBasedOnLanguage( |
135 | LanguageAwareText $pageContent, |
136 | ?Language $targetLanguage, |
137 | bool $useInline |
138 | ): string { |
139 | if ( $this->needsWrapping( $targetLanguage, $pageContent ) ) { |
140 | // Wrap page contents if it differs from target page's language. Ideally the |
141 | // message contents would be wrapped too, but we do not know its language. |
142 | return $this->wrapContentWithLanguageAttributes( $pageContent, $useInline ); |
143 | } else { |
144 | return $pageContent->getWikitext(); |
145 | } |
146 | } |
147 | |
148 | /** |
149 | * Check if the page contents need to be wrapped |
150 | * @param Language|null $targetLanguage |
151 | * @param LanguageAwareText $pageContent |
152 | * @return bool |
153 | */ |
154 | private function needsWrapping( ?Language $targetLanguage, LanguageAwareText $pageContent ): bool { |
155 | return !$targetLanguage || $targetLanguage->getCode() !== $pageContent->getLanguageCode(); |
156 | } |
157 | |
158 | /** |
159 | * Wrap contents with language attributes using inline or block elements |
160 | * @param LanguageAwareText $pageContent |
161 | * @param bool $useInline |
162 | * @return string |
163 | */ |
164 | private function wrapContentWithLanguageAttributes( LanguageAwareText $pageContent, bool $useInline ): string { |
165 | $elementToUse = 'div'; |
166 | $content = "\n" . $pageContent->getWikitext() . "\n"; |
167 | |
168 | if ( $useInline == self::USE_INLINE ) { |
169 | $elementToUse = 'span'; |
170 | $content = $pageContent->getWikitext(); |
171 | } |
172 | |
173 | return Html::rawElement( |
174 | $elementToUse, |
175 | [ |
176 | 'lang' => $pageContent->getLanguageCode(), |
177 | 'dir' => $pageContent->getLanguageDirection() |
178 | ], |
179 | $content |
180 | ); |
181 | } |
182 | } |