Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 147 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
TranslationsSpecialPage | |
0.00% |
0 / 147 |
|
0.00% |
0 / 8 |
342 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDescription | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
prefixSearchSubpages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
20 | |||
namespaceMessageForm | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
2 | |||
getSortedNamespaces | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
showTranslations | |
0.00% |
0 / 85 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\TranslatorInterface; |
5 | |
6 | use HtmlArmor; |
7 | use MediaWiki\Extension\Translate\MessageLoading\MessageHandle; |
8 | use MediaWiki\Extension\Translate\Utilities\Utilities; |
9 | use MediaWiki\Html\Html; |
10 | use MediaWiki\HTMLForm\HTMLForm; |
11 | use MediaWiki\Language\Language; |
12 | use MediaWiki\Languages\LanguageNameUtils; |
13 | use MediaWiki\Message\Message; |
14 | use MediaWiki\SpecialPage\IncludableSpecialPage; |
15 | use MediaWiki\Title\Title; |
16 | use SearchEngineFactory; |
17 | use Wikimedia\Rdbms\ILoadBalancer; |
18 | |
19 | /** |
20 | * Implements a special page which shows all translations for a message. |
21 | * Bits taken from SpecialPrefixindex.php and TranslateTasks.php |
22 | * |
23 | * @author Siebrand Mazeland |
24 | * @author Niklas Laxstörm |
25 | * @license GPL-2.0-or-later |
26 | * @ingroup SpecialPage TranslateSpecialPage |
27 | */ |
28 | class TranslationsSpecialPage extends IncludableSpecialPage { |
29 | private Language $contentLanguage; |
30 | private LanguageNameUtils $languageNameUtils; |
31 | private ILoadBalancer $loadBalancer; |
32 | private SearchEngineFactory $searchEngineFactory; |
33 | |
34 | public function __construct( |
35 | Language $contentLanguage, |
36 | LanguageNameUtils $languageNameUtils, |
37 | ILoadBalancer $loadBalancer, |
38 | SearchEngineFactory $searchEngineFactory |
39 | ) { |
40 | parent::__construct( 'Translations' ); |
41 | $this->contentLanguage = $contentLanguage; |
42 | $this->languageNameUtils = $languageNameUtils; |
43 | $this->loadBalancer = $loadBalancer; |
44 | $this->searchEngineFactory = $searchEngineFactory; |
45 | } |
46 | |
47 | protected function getGroupName() { |
48 | return 'translation'; |
49 | } |
50 | |
51 | public function getDescription() { |
52 | return $this->msg( 'translations' ); |
53 | } |
54 | |
55 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
56 | return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory ); |
57 | } |
58 | |
59 | /** |
60 | * Entry point : initialise variables and call subfunctions. |
61 | * @param string|null $par Message key. Becomes "MediaWiki:Allmessages" when called like |
62 | * Special:Translations/MediaWiki:Allmessages (default null) |
63 | */ |
64 | public function execute( $par ) { |
65 | $this->setHeaders(); |
66 | $this->outputHeader(); |
67 | |
68 | $out = $this->getOutput(); |
69 | $out->addModuleStyles( 'ext.translate.specialpages.styles' ); |
70 | |
71 | $par = (string)$par; |
72 | |
73 | if ( $this->including() ) { |
74 | $title = Title::newFromText( $par ); |
75 | if ( !$title ) { |
76 | $out->addWikiMsg( 'translate-translations-including-no-param' ); |
77 | } else { |
78 | $this->showTranslations( $title ); |
79 | } |
80 | |
81 | return; |
82 | } |
83 | |
84 | /** |
85 | * GET values. |
86 | */ |
87 | $request = $this->getRequest(); |
88 | $message = $request->getText( 'message', $par ); |
89 | $namespace = $request->getInt( 'namespace', NS_MAIN ); |
90 | |
91 | $title = Title::newFromText( $message, $namespace ); |
92 | |
93 | $out->addHelpLink( |
94 | 'Help:Extension:Translate/Statistics_and_reporting#Translations_in_all_languages' |
95 | ); |
96 | |
97 | if ( !$title ) { |
98 | $title = Title::makeTitle( NS_MEDIAWIKI, '' ); |
99 | $this->namespaceMessageForm( $title ); |
100 | } else { |
101 | $this->namespaceMessageForm( $title ); |
102 | $out->addHTML( '<br />' ); |
103 | $this->showTranslations( $title ); |
104 | } |
105 | } |
106 | |
107 | /** |
108 | * Message input fieldset |
109 | */ |
110 | private function namespaceMessageForm( Title $title ): void { |
111 | $formDescriptor = [ |
112 | 'textbox' => [ |
113 | 'type' => 'text', |
114 | 'name' => 'message', |
115 | 'id' => 'message', |
116 | 'label-message' => 'translate-translations-messagename', |
117 | 'size' => 30, |
118 | 'default' => $title->getText(), |
119 | ], |
120 | 'selector' => [ |
121 | 'type' => 'select', |
122 | 'name' => 'namespace', |
123 | 'id' => 'namespace', |
124 | 'label-message' => 'translate-translations-project', |
125 | 'options' => $this->getSortedNamespaces(), |
126 | 'default' => $title->getNamespace(), |
127 | ] |
128 | ]; |
129 | |
130 | HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) |
131 | ->setMethod( 'get' ) |
132 | ->setTitle( $this->getPageTitle() ) // Remove subpage |
133 | ->setSubmitTextMsg( 'allpagessubmit' ) |
134 | ->setWrapperLegendMsg( 'translate-translations-fieldset-title' ) |
135 | ->prepareForm() |
136 | ->displayForm( false ); |
137 | } |
138 | |
139 | /** |
140 | * Returns sorted array of namespaces. |
141 | * |
142 | * @return array<string,int> |
143 | */ |
144 | private function getSortedNamespaces(): array { |
145 | $nslist = []; |
146 | foreach ( $this->getConfig()->get( 'TranslateMessageNamespaces' ) as $ns ) { |
147 | $nslist[$this->contentLanguage->getFormattedNsText( $ns )] = $ns; |
148 | } |
149 | ksort( $nslist ); |
150 | |
151 | return $nslist; |
152 | } |
153 | |
154 | /** |
155 | * Builds a table with all translations of $title. |
156 | */ |
157 | private function showTranslations( Title $title ): void { |
158 | $handle = new MessageHandle( $title ); |
159 | $namespace = $title->getNamespace(); |
160 | $message = $handle->getKey(); |
161 | |
162 | if ( !$handle->isValid() ) { |
163 | $this->getOutput()->addWikiMsg( 'translate-translations-no-message', $title->getPrefixedText() ); |
164 | |
165 | return; |
166 | } |
167 | |
168 | $dbr = $this->loadBalancer->getConnection( DB_REPLICA ); |
169 | /** @var string[] */ |
170 | $titles = $dbr->newSelectQueryBuilder() |
171 | ->select( 'page_title' ) |
172 | ->from( 'page' ) |
173 | ->where( [ |
174 | 'page_namespace' => $namespace, |
175 | 'page_title ' . $dbr->buildLike( "$message/", $dbr->anyString() ), |
176 | ] ) |
177 | ->caller( __METHOD__ ) |
178 | ->orderBy( 'page_title' ) |
179 | ->fetchFieldValues(); |
180 | |
181 | if ( !$titles ) { |
182 | $this->getOutput()->addWikiMsg( |
183 | 'translate-translations-no-message', |
184 | $title->getPrefixedText() |
185 | ); |
186 | |
187 | return; |
188 | } else { |
189 | $this->getOutput()->addWikiMsg( |
190 | 'translate-translations-count', |
191 | Message::numParam( count( $titles ) ) |
192 | ); |
193 | } |
194 | |
195 | $pageInfo = Utilities::getContents( $titles, $namespace ); |
196 | |
197 | $rows = [ |
198 | Html::rawElement( |
199 | 'tr', |
200 | [], |
201 | Html::element( 'th', [], $this->msg( 'allmessagesname' )->text() ) . |
202 | Html::element( 'th', [], $this->msg( 'allmessagescurrent' )->text() ) |
203 | ), |
204 | ]; |
205 | |
206 | $historyText = "\u{00A0}<sup>" . |
207 | $this->msg( 'translate-translations-history-short' )->escaped() . |
208 | "</sup>\u{00A0}"; |
209 | $separator = $this->msg( 'word-separator' )->plain(); |
210 | |
211 | foreach ( $titles as $key ) { |
212 | $tTitle = Title::makeTitle( $namespace, $key ); |
213 | $tHandle = new MessageHandle( $tTitle ); |
214 | |
215 | $code = $tHandle->getCode(); |
216 | |
217 | $text = Utilities::getLanguageName( $code, $this->getLanguage()->getCode() ); |
218 | $text .= $separator; |
219 | $text .= $this->msg( 'parentheses' )->params( $code )->plain(); |
220 | $tools = [ |
221 | 'edit' => Html::element( |
222 | 'a', |
223 | [ 'href' => Utilities::getEditorUrl( $tHandle ) ], |
224 | $text |
225 | ), |
226 | 'history' => $this->getLinkRenderer()->makeLink( |
227 | $tTitle, |
228 | new HtmlArmor( $historyText ), |
229 | [ |
230 | 'title' => $this->msg( 'history-title', $tTitle->getPrefixedDBkey() )->text() |
231 | ], |
232 | [ 'action' => 'history' ] |
233 | ), |
234 | ]; |
235 | |
236 | $class = ''; |
237 | if ( MessageHandle::hasFuzzyString( $pageInfo[$key][0] ) || $tHandle->isFuzzy() ) { |
238 | $class = 'mw-sp-translate-fuzzy'; |
239 | } |
240 | |
241 | $languageAttributes = []; |
242 | if ( $this->languageNameUtils->isKnownLanguageTag( $code ) ) { |
243 | $language = $tHandle->getEffectiveLanguage(); |
244 | $languageAttributes = [ |
245 | 'lang' => $language->getHtmlCode(), |
246 | 'dir' => $language->getDir(), |
247 | ]; |
248 | } |
249 | |
250 | $formattedContent = Utilities::convertWhiteSpaceToHTML( $pageInfo[$key][0] ); |
251 | |
252 | $rows[] = Html::rawElement( |
253 | 'tr', |
254 | [ 'class' => $class ], |
255 | Html::rawElement( 'td', [], $tools['history'] . $tools['edit'] ) . |
256 | Html::rawElement( 'td', $languageAttributes, $formattedContent ) |
257 | ); |
258 | } |
259 | |
260 | $out = Html::rawElement( |
261 | 'table', |
262 | [ 'class' => 'mw-sp-translate-table sortable wikitable' ], |
263 | "\n" . implode( "\n", $rows ) . "\n" |
264 | ); |
265 | $this->getOutput()->addHTML( $out ); |
266 | } |
267 | } |