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