Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 147
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
TranslationsSpecialPage
0.00% covered (danger)
0.00%
0 / 147
0.00% covered (danger)
0.00%
0 / 8
342
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDescription
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 prefixSearchSubpages
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
20
 namespaceMessageForm
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
2
 getSortedNamespaces
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 showTranslations
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorInterface;
5
6use HtmlArmor;
7use MediaWiki\Extension\Translate\MessageLoading\MessageHandle;
8use MediaWiki\Extension\Translate\Utilities\Utilities;
9use MediaWiki\Html\Html;
10use MediaWiki\HTMLForm\HTMLForm;
11use MediaWiki\Language\Language;
12use MediaWiki\Languages\LanguageNameUtils;
13use MediaWiki\Message\Message;
14use MediaWiki\SpecialPage\IncludableSpecialPage;
15use MediaWiki\Title\Title;
16use SearchEngineFactory;
17use 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 */
28class 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}