Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 182
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
TranslatePage
0.00% covered (danger)
0.00%
0 / 182
0.00% covered (danger)
0.00%
0 / 5
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 114
0.00% covered (danger)
0.00%
0 / 1
272
 getTitle
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 showLanguageSelector
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
6
 doSubmit
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\SecurePoll\Pages;
4
5use MediaWiki\Extension\SecurePoll\Exceptions\InvalidDataException;
6use MediaWiki\Extension\SecurePoll\SpecialSecurePoll;
7use MediaWiki\Extension\SecurePoll\TranslationRepo;
8use MediaWiki\Languages\LanguageNameUtils;
9use MediaWiki\Linker\Linker;
10use MediaWiki\MediaWikiServices;
11use MediaWiki\Message\Message;
12use MediaWiki\Title\Title;
13use MediaWiki\WikiMap\WikiMap;
14use OOUI\ButtonInputWidget;
15use OOUI\DropdownInputWidget;
16use OOUI\Element;
17use OOUI\FieldLayout;
18use OOUI\FieldsetLayout;
19use OOUI\FormLayout;
20use OOUI\HorizontalLayout;
21use OOUI\HtmlSnippet;
22use OOUI\LabelWidget;
23use OOUI\MultilineTextInputWidget;
24use OOUI\Tag;
25use OOUI\TextInputWidget;
26
27/**
28 * A SecurePoll subpage for translating election messages.
29 */
30class TranslatePage extends ActionPage {
31
32    /** @var bool|null */
33    public $isAdmin;
34
35    /** @var LanguageNameUtils */
36    private $languageNameUtils;
37
38    /** @var TranslationRepo */
39    private $translationRepo;
40
41    /**
42     * @param SpecialSecurePoll $specialPage
43     * @param LanguageNameUtils $languageNameUtils
44     * @param TranslationRepo $translationRepo
45     */
46    public function __construct(
47        SpecialSecurePoll $specialPage,
48        LanguageNameUtils $languageNameUtils,
49        TranslationRepo $translationRepo
50    ) {
51        parent::__construct( $specialPage );
52        $this->languageNameUtils = $languageNameUtils;
53        $this->translationRepo = $translationRepo;
54    }
55
56    /**
57     * Execute the subpage.
58     * @param array $params Array of subpage parameters.
59     */
60    public function execute( $params ) {
61        $out = $this->specialPage->getOutput();
62        $request = $this->specialPage->getRequest();
63        $out->enableOOUI();
64
65        if ( !count( $params ) ) {
66            $out->addWikiMsg( 'securepoll-too-few-params' );
67
68            return;
69        }
70
71        $electionId = intval( $params[0] );
72        $this->election = $this->context->getElection( $electionId );
73        if ( !$this->election ) {
74            $out->addWikiMsg( 'securepoll-invalid-election', $electionId );
75
76            return;
77        }
78        $this->initLanguage( $this->specialPage->getUser(), $this->election );
79        $out->setPageTitleMsg( $this->msg( 'securepoll-translate-title', $this->election->getMessage( 'title' ) ) );
80
81        $jumpUrl = $this->election->getProperty( 'jump-url' );
82        if ( $jumpUrl ) {
83            $jumpId = $this->election->getProperty( 'jump-id' );
84            if ( !$jumpId ) {
85                throw new InvalidDataException( 'Configuration error: no jump-id' );
86            }
87            $jumpUrl .= "/edit/$jumpId";
88            if ( count( $params ) > 1 ) {
89                $jumpUrl .= '/' . implode( '/', array_slice( $params, 1 ) );
90            }
91
92            $wiki = $this->election->getProperty( 'main-wiki' );
93            if ( $wiki ) {
94                $wiki = WikiMap::getWikiName( $wiki );
95            } else {
96                $wiki = $this->msg( 'securepoll-edit-redirect-otherwiki' )->text();
97            }
98
99            $out->addWikiMsg(
100                'securepoll-edit-redirect',
101                Message::rawParam( Linker::makeExternalLink( $jumpUrl, $wiki ) )
102            );
103
104            return;
105        }
106
107        $this->isAdmin = $this->election->isAdmin( $this->specialPage->getUser() );
108
109        $primary = $this->election->getLanguage();
110        $secondary = $request->getVal( 'secondary_lang' );
111        if ( $secondary !== null ) {
112            # Language selector submitted: redirect to the subpage
113            $out->redirect( $this->getTitle( $secondary )->getFullURL() );
114
115            return;
116        }
117
118        if ( !isset( $params[1] ) ) {
119            # No language selected, show the selector
120            $this->showLanguageSelector( $primary );
121            $sourceConfig = MediaWikiServices::getInstance()->getMainConfig()
122            ->get( 'SecurePollTranslationImportSourceUrl' );
123            $out->addJsConfigVars( 'SecurePollTranslationImportSourceUrl', $sourceConfig );
124
125            $out->addJsConfigVars( 'SecurePollSubPage', 'translate' );
126            $out->addModules( 'ext.securepoll.htmlform' );
127            return;
128        }
129
130        $secondary = $params[1];
131        $inLanguage = $this->specialPage->getLanguage()->getCode();
132        $primaryName = $this->languageNameUtils->getLanguageName( $primary, $inLanguage );
133        $secondaryName = $this->languageNameUtils->getLanguageName( $secondary, $inLanguage );
134        if ( $secondaryName === '' ) {
135            $out->addWikiMsg( 'securepoll-invalid-language', $secondary );
136            $this->showLanguageSelector( $primary );
137            return;
138        }
139
140        # Set a subtitle to return to the language selector
141        $this->specialPage->setSubtitle(
142            [
143                $this->getTitle(),
144                $this->msg(
145                    'securepoll-translate-title',
146                    $this->election->getMessage( 'title' )
147                )->text()
148            ]
149        );
150
151        # If the request was posted, do the submission
152        if ( $request->wasPosted() && $request->getVal( 'action' ) == 'submit' ) {
153            $this->doSubmit( $secondary );
154            return;
155        }
156
157        # Show the form
158        $action = $this->getTitle( $secondary )->getLocalURL( 'action=submit' );
159        $form = new FormLayout( [ 'method' => 'post', 'action' => $action ] );
160
161        $table = new Tag( 'table' );
162        $table->addClasses( [ 'mw-datatable', 'TablePager', 'securepoll-trans-table' ] )->appendContent(
163            ( new Tag( 'thead' ) )->appendContent( ( new Tag( 'tr' ) )->appendContent(
164                ( new Tag( 'th' ) )->appendContent( $this->msg( 'securepoll-header-trans-id' ) ),
165                ( new Tag( 'th' ) )->appendContent( $primaryName ),
166                ( new Tag( 'th' ) )->appendContent( $secondaryName )
167            ) )
168        );
169        $tbody = new Tag( 'tbody' );
170        $table->appendContent( $tbody );
171
172        $entities = array_merge( [ $this->election ], $this->election->getDescendants() );
173        foreach ( $entities as $entity ) {
174            $entityName = $entity->getType() . '(' . $entity->getId() . ')';
175            foreach ( $entity->getMessageNames() as $messageName ) {
176                $tbody->appendContent( ( new Tag( 'tr' ) )->appendContent(
177                    ( new Tag( 'td' ) )->addClasses( [ 'trans-id' ] )
178                            ->appendContent( "$entityName/$messageName" ),
179                    ( new Tag( 'td' ) )->addClasses( [ 'trans-origin' ] )->appendContent( new HtmlSnippet(
180                        nl2br( htmlspecialchars( $entity->getRawMessage( $messageName, $primary ) ) )
181                    ) ),
182                    ( new Tag( 'td' ) )->addClasses( [ 'trans-lang' ] )->appendContent(
183                        new MultilineTextInputWidget( [
184                            'name' => 'trans_' . $entity->getId() . '_' . $messageName,
185                            'value' => $entity->getRawMessage( $messageName, $secondary ),
186                            'classes' => [ 'securepoll-translate-box' ],
187                            'readonly' => !$this->isAdmin,
188                            'autosize' => true
189                        ] )
190                    ) )
191                );
192            }
193        }
194
195        $fields = new FieldsetLayout();
196
197        $fields->addItems( [ new Element( [ 'content' => [ $table ] ] ) ] );
198
199        if ( $this->isAdmin && $this->specialPage->getConfig()->get( 'SecurePollUseNamespace' ) ) {
200            $fields->addItems( [ new FieldLayout( new TextInputWidget( [
201                'name' => 'comment',
202                'maxlength' => 250,
203            ] ), [
204                'label' => $this->msg( 'securepoll-translate-label-comment' ),
205                'align' => 'top',
206            ] ) ] );
207        }
208
209        $fields->addItems( [ new FieldLayout( new ButtonInputWidget( [
210            'label' => $this->msg( 'securepoll-submit-translate' )->text(),
211            'flags' => [ 'primary', 'progressive' ],
212            'type' => 'submit',
213            'disabled' => !$this->isAdmin,
214        ] ) ) ] );
215
216        $form->appendContent( $fields );
217
218        $out->addHTML( $form );
219    }
220
221    /**
222     * @param string|false $lang
223     * @return Title
224     */
225    public function getTitle( $lang = false ) {
226        $subpage = 'translate/' . $this->election->getId();
227        if ( $lang !== false ) {
228            $subpage .= '/' . $lang;
229        }
230
231        return $this->specialPage->getPageTitle( $subpage );
232    }
233
234    /**
235     * Show a language selector to allow the user to choose the language to
236     * translate.
237     * @param string $selectedCode
238     */
239    public function showLanguageSelector( $selectedCode ) {
240        $languages = $this->languageNameUtils->getLanguageNames();
241        ksort( $languages );
242
243        $apiEndpoint = $this->specialPage->getConfig()->get( 'SecurePollTranslationImportSourceUrl' );
244
245        $form = new FormLayout( [
246            'action' => $this->getTitle( false )->getLocalURL(),
247            'method' => 'get',
248            'items' => [ new FieldsetLayout( [ 'items' => [
249                new HorizontalLayout( [
250                    'id' => 'sp-translation-selection',
251                    'items' => [
252                        new DropdownInputWidget( [
253                            'name' => 'secondary_lang',
254                            'value' => $selectedCode,
255                            'options' => array_map( static function ( $code, $name ) {
256                                return [
257                                    'label' => "$code - $name",
258                                    'data' => $code,
259                                ];
260                            }, array_keys( $languages ), $languages )
261                        ] ),
262                        new ButtonInputWidget( [
263                            'label' => $this->msg( 'securepoll-submit-select-lang' )->text(),
264                            'flags' => [ 'primary' ],
265                            'type' => 'submit',
266                        ] )
267                    ]
268                ] )
269            ] ] ) ]
270        ] );
271
272        if ( $apiEndpoint !== '' ) {
273            $form->addItems( [
274                new FieldLayout(
275                    new LabelWidget( [
276                        'label' => $this->msg( 'securepoll-subpage-translate-info', $apiEndpoint )->text()
277                    ] )
278                ),
279                new FieldLayout(
280                    new ButtonInputWidget( [
281                        'id' => 'import-trans-btn',
282                        'infusable' => true,
283                        'label' => $this->msg( 'securepoll-translate-import-button-label' )->text(),
284                        'flags' => [ 'primary', 'progressive' ],
285                        'disabled' => !$this->isAdmin
286                    ] )
287                )
288            ], 0 );
289        }
290        $this->specialPage->getOutput()->addHTML( $form );
291    }
292
293    /**
294     * Submit message text changes.
295     * @param string $secondary
296     */
297    public function doSubmit( $secondary ) {
298        $out = $this->specialPage->getOutput();
299
300        if ( !$this->isAdmin ) {
301            $out->addWikiMsg( 'securepoll-need-admin' );
302            return;
303        }
304
305        $request = $this->specialPage->getRequest();
306        $data = $request->getValues();
307
308        $this->translationRepo->setTranslation(
309            $this->election,
310            $data,
311            $secondary,
312            $this->specialPage->getContext()->getUser(),
313            $this->specialPage->getContext()->getRequest()->getText( 'comment' )
314        );
315
316        $out->redirect( $this->getTitle( $secondary )->getFullURL() );
317    }
318}