Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
30.19% |
96 / 318 |
|
14.29% |
2 / 14 |
CRAP | |
0.00% |
0 / 1 |
SpecialEditLexicon | |
30.19% |
96 / 318 |
|
14.29% |
2 / 14 |
1162.42 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
28.89% |
13 / 45 |
|
0.00% |
0 / 1 |
18.95 | |||
formSteps | |
0.00% |
0 / 41 |
|
0.00% |
0 / 1 |
132 | |||
syncSubmit | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
42 | |||
redirectToLoginPage | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
4.00 | |||
getSyncFields | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
2 | |||
getLookupFields | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
2 | |||
getSelectFields | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
20 | |||
getAddFields | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
2 | |||
getEditFields | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
12 | |||
submit | |
83.87% |
52 / 62 |
|
0.00% |
0 / 1 |
15.94 | |||
purgeOriginPageUtterances | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getLanguageOptions | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
success | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Wikispeech\Specials; |
4 | |
5 | /** |
6 | * @file |
7 | * @ingroup Extensions |
8 | * @license GPL-2.0-or-later |
9 | */ |
10 | |
11 | use Exception; |
12 | use HTMLForm; |
13 | use InvalidArgumentException; |
14 | use MediaWiki\Html\Html; |
15 | use MediaWiki\Languages\LanguageNameUtils; |
16 | use MediaWiki\Logger\LoggerFactory; |
17 | use MediaWiki\Wikispeech\Lexicon\ConfiguredLexiconStorage; |
18 | use MediaWiki\Wikispeech\Lexicon\LexiconEntry; |
19 | use MediaWiki\Wikispeech\Lexicon\LexiconEntryItem; |
20 | use MediaWiki\Wikispeech\Lexicon\LexiconStorage; |
21 | use MediaWiki\Wikispeech\Lexicon\NullEditLexiconException; |
22 | use MediaWiki\Wikispeech\SpeechoidConnector; |
23 | use MediaWiki\Wikispeech\Utterance\UtteranceStore; |
24 | use Psr\Log\LoggerInterface; |
25 | use RuntimeException; |
26 | use SpecialPage; |
27 | |
28 | /** |
29 | * Special page for editing the lexicon. |
30 | * |
31 | * @since 0.1.8 |
32 | */ |
33 | |
34 | class SpecialEditLexicon extends SpecialPage { |
35 | |
36 | /** @var LanguageNameUtils */ |
37 | private $languageNameUtils; |
38 | |
39 | /** @var LexiconStorage */ |
40 | private $lexiconStorage; |
41 | |
42 | /** @var SpeechoidConnector */ |
43 | private $speechoidConnector; |
44 | |
45 | /** @var LexiconEntryItem */ |
46 | private $modifiedItem; |
47 | |
48 | /** @var LoggerInterface */ |
49 | private $logger; |
50 | |
51 | /** @var string */ |
52 | private $postHtml; |
53 | |
54 | /** |
55 | * @since 0.1.12 Removed ConfigFactory |
56 | * @since 0.1.8 |
57 | * @param LanguageNameUtils $languageNameUtils |
58 | * @param LexiconStorage $lexiconStorage |
59 | * @param SpeechoidConnector $speechoidConnector |
60 | */ |
61 | public function __construct( |
62 | $languageNameUtils, |
63 | $lexiconStorage, |
64 | $speechoidConnector |
65 | ) { |
66 | parent::__construct( 'EditLexicon', 'wikispeech-edit-lexicon' ); |
67 | $this->languageNameUtils = $languageNameUtils; |
68 | $this->lexiconStorage = $lexiconStorage; |
69 | $this->speechoidConnector = $speechoidConnector; |
70 | $this->logger = LoggerFactory::getInstance( 'Wikispeech' ); |
71 | $this->postHtml = ''; |
72 | } |
73 | |
74 | /** |
75 | * @since 0.1.8 |
76 | * @param string|null $subpage |
77 | */ |
78 | public function execute( $subpage ) { |
79 | $this->setHeaders(); |
80 | if ( $this->redirectToLoginPage() ) { |
81 | return; |
82 | } |
83 | |
84 | $this->checkPermissions(); |
85 | $this->addHelpLink( 'Help:Extension:Wikispeech/Lexicon editor' ); |
86 | try { |
87 | $this->speechoidConnector->requestLexicons(); |
88 | } catch ( Exception $e ) { |
89 | $this->logger->error( 'Speechoid is down.' ); |
90 | $this->getOutput()->showErrorPage( |
91 | 'error', |
92 | 'wikispeech-edit-lexicon-speechoid-down' |
93 | ); |
94 | return; |
95 | } |
96 | |
97 | $request = $this->getRequest(); |
98 | $language = $request->getText( 'language' ); |
99 | $word = $request->getText( 'word' ); |
100 | |
101 | try { |
102 | $this->lexiconStorage->getEntry( $language, $word ); |
103 | $formData = $this->formSteps( $language, $word ); |
104 | } catch ( RuntimeException $e ) { |
105 | $this->getOutput()->addHtml( Html::errorBox( |
106 | $this->msg( 'wikispeech-lexicon-sync-error' )->parse() |
107 | ) ); |
108 | |
109 | $formData = $this->getSyncFields( $language, $word ); |
110 | } |
111 | |
112 | $fields = $formData['fields']; |
113 | $formId = $formData['formId']; |
114 | $submitMessage = $formData['submitMessage']; |
115 | $submitCb = $formData['submitCb']; |
116 | |
117 | $copyrightNote = $this->msg( 'wikispeech-lexicon-copyrightnote' )->parse(); |
118 | $this->postHtml .= Html::rawElement( 'p', [], $copyrightNote ); |
119 | |
120 | $form = HTMLForm::factory( |
121 | 'ooui', |
122 | $fields, |
123 | $this->getContext() |
124 | ); |
125 | |
126 | $form->setFormIdentifier( $formId ); |
127 | $form->setSubmitCallback( [ $this, $submitCb ] ); |
128 | $form->setSubmitTextMsg( $submitMessage ); |
129 | $form->setPostHtml( $this->postHtml ); |
130 | |
131 | if ( $form->show() ) { |
132 | if ( $formId === 'editItem' ) { |
133 | $this->success( 'wikispeech-lexicon-edit-entry-success' ); |
134 | } else { |
135 | $this->success( 'wikispeech-lexicon-add-entry-success' ); |
136 | } |
137 | } |
138 | |
139 | $this->getOutput()->addModules( [ |
140 | 'ext.wikispeech.specialEditLexicon' |
141 | ] ); |
142 | } |
143 | |
144 | /** |
145 | * @since 0.1.12 |
146 | * @return array |
147 | */ |
148 | private function formSteps( string $language, string $word ) { |
149 | $request = $this->getRequest(); |
150 | $formId = ''; |
151 | $submitMessage = 'wikispeech-lexicon-next'; |
152 | $submitCb = 'submit'; |
153 | |
154 | if ( $request->getText( 'id' ) === '' ) { |
155 | $id = ''; |
156 | } else { |
157 | $id = $request->getIntOrNull( 'id' ); |
158 | } |
159 | |
160 | try { |
161 | $entry = $this->lexiconStorage->getEntry( $language, $word ); |
162 | } catch ( RuntimeException $e ) { |
163 | $entry = null; |
164 | } |
165 | |
166 | if ( !$language || !$word ) { |
167 | $formId = 'lookup'; |
168 | $fields = $this->getLookupFields(); |
169 | } elseif ( $entry === null ) { |
170 | $formId = 'newEntry'; |
171 | $fields = $this->getAddFields( $language, $word ); |
172 | $submitMessage = 'wikispeech-lexicon-save'; |
173 | } elseif ( !in_array( 'id', $request->getValueNames() ) ) { |
174 | $formId = 'selectItem'; |
175 | $fields = $this->getSelectFields( $language, $word, $entry ); |
176 | } elseif ( $id ) { |
177 | $formId = 'editItem'; |
178 | $fields = $this->getEditFields( $language, $word, $id ); |
179 | $submitMessage = 'wikispeech-lexicon-save'; |
180 | } elseif ( $id === '' ) { |
181 | $formId = 'newItem'; |
182 | $fields = $this->getAddFields( $language, $word ); |
183 | $submitMessage = 'wikispeech-lexicon-save'; |
184 | } else { |
185 | // We have a set of parameters that we can't do anything with. Show the first page. |
186 | $formId = 'lookup'; |
187 | $fields = $this->getLookupFields(); |
188 | } |
189 | |
190 | // Set default values from the parameters. |
191 | foreach ( $fields as $field ) { |
192 | $name = $field['name']; |
193 | $value = $request->getVal( $name ); |
194 | if ( $value !== null ) { |
195 | // There's no extra conversion logic so default values |
196 | // are set to strings and handled down the |
197 | // line. E.g. boolean values are true for "false" or |
198 | // "no". |
199 | $fields[$name]['default'] = $value; |
200 | } |
201 | } |
202 | |
203 | return [ |
204 | 'fields' => $fields, |
205 | 'formId' => $formId, |
206 | 'submitMessage' => $submitMessage, |
207 | 'submitCb' => $submitCb |
208 | ]; |
209 | } |
210 | |
211 | /** |
212 | * Overwrite the local entry with speechoid entry |
213 | * |
214 | * @since 0.1.12 |
215 | */ |
216 | public function syncSubmit() { |
217 | if ( !$this->lexiconStorage instanceof ConfiguredLexiconStorage ) { |
218 | return; |
219 | } |
220 | $request = $this->getRequest(); |
221 | $language = $request->getText( 'language' ); |
222 | $word = $request->getText( 'word' ); |
223 | |
224 | try { |
225 | |
226 | $localEntry = $this->lexiconStorage->getLocalEntry( $language, $word ); |
227 | |
228 | if ( $localEntry === null ) { |
229 | throw new RuntimeException( "Local entry is missing." ); |
230 | } |
231 | |
232 | foreach ( $localEntry->getItems() as $localEntryItem ) { |
233 | $speechoidId = $localEntryItem->getSpeechoidIdentity(); |
234 | if ( !$speechoidId ) { |
235 | throw new InvalidArgumentException( "Cannot sync item without Speechoid identity" ); |
236 | } |
237 | $this->lexiconStorage->syncEntryItem( $language, $word, $speechoidId ); |
238 | } |
239 | |
240 | $this->getOutput()->redirect( |
241 | $this->getPageTitle()->getFullURL( [ |
242 | 'language' => $language, |
243 | 'word' => $word |
244 | ] ) |
245 | ); |
246 | |
247 | } catch ( InvalidArgumentException $e ) { |
248 | $this->getOutput()->addHtml( Html::errorBox( |
249 | $this->msg( 'wikispeech-lexicon-sync-error-unidentified' ) |
250 | ) ); |
251 | } |
252 | } |
253 | |
254 | /** |
255 | * Redirect the user to login page when appropriate |
256 | * |
257 | * If the the user is not logged in and config variable |
258 | * `WikispeechEditLexiconAutoLogin` is true this adds a redirect |
259 | * to the output. Returns to this special page after login and |
260 | * keeps any URL parameters that were originally given. |
261 | * |
262 | * @since 0.1.11 |
263 | * @return bool True if a redirect was added, else false. |
264 | */ |
265 | private function redirectToLoginPage(): bool { |
266 | if ( $this->getUser()->isNamed() ) { |
267 | // User already logged in. |
268 | return false; |
269 | } |
270 | |
271 | if ( !$this->getConfig()->get( 'WikispeechEditLexiconAutoLogin' ) ) { |
272 | return false; |
273 | } |
274 | |
275 | $lexiconUrl = $this->getPageTitle()->getPrefixedText(); |
276 | $lexiconRequest = $this->getRequest(); |
277 | |
278 | $loginParemeters = [ |
279 | 'returnto' => $lexiconUrl |
280 | ]; |
281 | if ( $lexiconRequest->getValues() ) { |
282 | // Add any parameters to this page to include after |
283 | // logging in. |
284 | $lexiconParametersString = http_build_query( $lexiconRequest->getValues() ); |
285 | $loginParemeters['returntoquery'] = $lexiconParametersString; |
286 | } |
287 | $title = SpecialPage::getTitleFor( 'Userlogin' ); |
288 | $loginUrl = $title->getFullURL( $loginParemeters ); |
289 | $this->getOutput()->redirect( $loginUrl ); |
290 | |
291 | return true; |
292 | } |
293 | |
294 | /** |
295 | * Create a field descriptor for sync form data fields |
296 | * |
297 | * @param string $language |
298 | * @param string $word |
299 | * @since 0.1.12 |
300 | * @return array |
301 | */ |
302 | private function getSyncFields( $language, $word ): array { |
303 | $formData = [ |
304 | 'fields' => [ |
305 | 'language' => [ |
306 | 'type' => 'hidden', |
307 | 'name' => 'language', |
308 | 'default' => $language |
309 | ], |
310 | 'word' => [ |
311 | 'type' => 'hidden', |
312 | 'name' => 'word', |
313 | 'default' => $word |
314 | ] |
315 | ], |
316 | 'formId' => 'syncForm', |
317 | 'submitMessage' => $this->msg( 'wikispeech-lexicon-sync-error-button' ), |
318 | 'submitCb' => "syncSubmit" |
319 | ]; |
320 | return $formData; |
321 | } |
322 | |
323 | /** |
324 | * Create a field descriptor for looking up a word |
325 | * |
326 | * Has one field for language and one for word. |
327 | * |
328 | * @since 0.1.10 |
329 | * @return array |
330 | */ |
331 | private function getLookupFields(): array { |
332 | $fields = [ |
333 | 'language' => [ |
334 | 'name' => 'language', |
335 | 'type' => 'select', |
336 | 'label' => $this->msg( 'wikispeech-language' )->text(), |
337 | 'options' => $this->getLanguageOptions(), |
338 | 'id' => 'ext-wikispeech-language' |
339 | ], |
340 | 'word' => [ |
341 | 'name' => 'word', |
342 | 'type' => 'text', |
343 | 'label' => $this->msg( 'wikispeech-word' )->text(), |
344 | 'required' => true |
345 | ], |
346 | 'page' => [ |
347 | 'name' => 'page', |
348 | 'type' => 'hidden' |
349 | ] |
350 | ]; |
351 | return $fields; |
352 | } |
353 | |
354 | /** |
355 | * Create a field descriptor for selecting an item |
356 | * |
357 | * Has a field for selecting the id of the item to edit or "new" |
358 | * for creating a new item. Also shows fields for language and |
359 | * word from previous page, but readonly. |
360 | * |
361 | * @since 0.1.10 |
362 | * @param string $language |
363 | * @param string $word |
364 | * @param LexiconEntry|null $entry |
365 | * @return array |
366 | */ |
367 | private function getSelectFields( |
368 | string $language, |
369 | string $word, |
370 | ?LexiconEntry $entry = null |
371 | ): array { |
372 | $fields = $this->getLookupFields(); |
373 | $fields['language']['readonly'] = true; |
374 | $fields['language']['type'] = 'text'; |
375 | $fields['word']['readonly'] = true; |
376 | $fields['word']['required'] = false; |
377 | |
378 | $newLabel = $this->msg( 'wikispeech-lexicon-new' )->text(); |
379 | $itemOptions = [ $newLabel => '' ]; |
380 | if ( $entry ) { |
381 | foreach ( $entry->getItems() as $item ) { |
382 | $properties = $item->getProperties(); |
383 | if ( !isset( $properties->id ) ) { |
384 | $this->logger->warning( |
385 | __METHOD__ . ': Skipping item with no id.' |
386 | ); |
387 | continue; |
388 | } |
389 | $id = $properties->id; |
390 | // Add item id as option for selection. |
391 | $itemOptions[$id] = $id; |
392 | // Add item to info text. |
393 | $this->postHtml .= Html::element( 'pre', [], $item ); |
394 | } |
395 | } |
396 | |
397 | $fields['id'] = [ |
398 | 'name' => 'id', |
399 | 'type' => 'select', |
400 | 'label' => $this->msg( 'wikispeech-item-id' )->text(), |
401 | 'options' => $itemOptions, |
402 | 'default' => '' |
403 | ]; |
404 | return $fields; |
405 | } |
406 | |
407 | /** |
408 | * Create a field descriptor for adding an entry or item |
409 | * |
410 | * Has fields for transcription and preferred. Item id is held by |
411 | * a hidden field. Also shows fields for language and word from |
412 | * previous page, but readonly. |
413 | * |
414 | * @since 0.1.10 |
415 | * @param string $language |
416 | * @param string $word |
417 | * @return array |
418 | */ |
419 | private function getAddFields( string $language, string $word ): array { |
420 | $fields = $this->getSelectFields( $language, $word ); |
421 | $fields['id']['type'] = 'hidden'; |
422 | $fields += [ |
423 | 'transcription' => [ |
424 | 'name' => 'transcription', |
425 | 'type' => 'textwithbutton', |
426 | 'label' => $this->msg( 'wikispeech-transcription' )->text(), |
427 | 'required' => true, |
428 | 'id' => 'ext-wikispeech-transcription', |
429 | 'buttontype' => 'button', |
430 | 'buttondefault' => $this->msg( 'wikispeech-preview' )->text(), |
431 | 'buttonid' => 'ext-wikispeech-preview-button' |
432 | ], |
433 | 'preferred' => [ |
434 | 'name' => 'preferred', |
435 | 'type' => 'check', |
436 | 'label' => $this->msg( 'wikispeech-preferred' )->text() |
437 | ] |
438 | ]; |
439 | return $fields; |
440 | } |
441 | |
442 | /** |
443 | * Create a field descriptor for editing an item |
444 | * |
445 | * Has fields for transcription and preferred with default values |
446 | * from the lexicon. Item id is held by a hidden field. Also shows |
447 | * fields for language and word from previous page, but readonly. |
448 | * |
449 | * @since 0.1.10 |
450 | * @param string $language |
451 | * @param string $word |
452 | * @param int $id |
453 | * @return array |
454 | */ |
455 | private function getEditFields( string $language, string $word, int $id ): array { |
456 | $fields = $this->getAddFields( $language, $word ); |
457 | $entry = $this->lexiconStorage->getEntry( $language, $word ); |
458 | $item = $entry->findItemBySpeechoidIdentity( $id ); |
459 | if ( $item === null ) { |
460 | $this->getOutput()->addHTML( Html::errorBox( |
461 | $this->getOutput()->msg( 'wikispeech-edit-lexicon-no-item-found' )->params( $id )->parse() |
462 | ) ); |
463 | return $this->getSelectFields( $language, $word, $entry ); |
464 | } |
465 | $transcriptionStatus = $this->speechoidConnector->toIpa( |
466 | $item->getTranscription(), |
467 | $language |
468 | ); |
469 | if ( $transcriptionStatus->isOk() ) { |
470 | $transcription = $transcriptionStatus->getValue(); |
471 | } else { |
472 | $transcription = ''; |
473 | $this->getOutput()->addHTML( Html::errorBox( |
474 | $this->getOutput()->msg( 'wikispeech-edit-lexicon-transcription-unretrievable' )->params( $id )->parse() |
475 | ) ); |
476 | } |
477 | |
478 | $fields['transcription']['default'] = $transcription; |
479 | $fields['preferred']['default'] = $item->getPreferred(); |
480 | return $fields; |
481 | } |
482 | |
483 | /** |
484 | * Handle submit request |
485 | * |
486 | * If there is no entry for the given word a new one is created |
487 | * with a new item. If the request contains an id that item is |
488 | * updated or, if id is empty, a new item is created. If there |
489 | * isn't enough information to do any of the above this returns |
490 | * false which sends the user to the appropriate page via |
491 | * `execute()`. |
492 | * |
493 | * @since 0.1.9 |
494 | * @param array $data |
495 | * @return bool |
496 | */ |
497 | public function submit( array $data ): bool { |
498 | if ( |
499 | !array_key_exists( 'language', $data ) || |
500 | !array_key_exists( 'word', $data ) || |
501 | !array_key_exists( 'id', $data ) || |
502 | !array_key_exists( 'transcription', $data ) || |
503 | $data['transcription'] === null || |
504 | !array_key_exists( 'preferred', $data ) |
505 | ) { |
506 | // We don't have all the information we need to make an |
507 | // edit yet. |
508 | return false; |
509 | } |
510 | |
511 | $language = $data['language']; |
512 | $transcription = $data['transcription']; |
513 | $sampaStatus = $this->speechoidConnector->fromIpa( |
514 | $transcription, |
515 | $language |
516 | ); |
517 | if ( !$sampaStatus->isOk() ) { |
518 | // TODO: Show error message (T308562). |
519 | return false; |
520 | } |
521 | |
522 | $sampa = $sampaStatus->getValue(); |
523 | $word = $data['word']; |
524 | $id = $data['id']; |
525 | $preferred = $data['preferred']; |
526 | if ( $id === '' ) { |
527 | // Empty id, create new item. |
528 | $item = new LexiconEntryItem(); |
529 | $properties = [ |
530 | 'strn' => $word, |
531 | 'transcriptions' => [ (object)[ 'strn' => $sampa ] ], |
532 | // Status is required by Speechoid. |
533 | 'status' => (object)[ |
534 | 'name' => 'ok' |
535 | ] |
536 | ]; |
537 | if ( $preferred ) { |
538 | $properties['preferred'] = true; |
539 | } |
540 | $item->setProperties( (object)$properties ); |
541 | $this->lexiconStorage->createEntryItem( |
542 | $language, |
543 | $word, |
544 | $item |
545 | ); |
546 | } else { |
547 | // Id already exists, update item. |
548 | $entry = $this->lexiconStorage->getEntry( $language, $word ); |
549 | $item = $entry->findItemBySpeechoidIdentity( intval( $id ) ); |
550 | if ( $item === null ) { |
551 | throw new RuntimeException( "No item with id '$id' found." ); |
552 | } |
553 | |
554 | $properties = $item->getProperties(); |
555 | $properties->transcriptions[0]->strn = $sampa; |
556 | if ( $preferred ) { |
557 | $properties->preferred = true; |
558 | } else { |
559 | unset( $properties->preferred ); |
560 | } |
561 | |
562 | $item->setProperties( $properties ); |
563 | try { |
564 | $this->lexiconStorage->updateEntryItem( |
565 | $language, |
566 | $word, |
567 | $item |
568 | ); |
569 | } catch ( NullEditLexiconException $e ) { |
570 | $this->getOutput()->addHtml( |
571 | Html::warningBox( |
572 | $this->msg( 'wikispeech-lexicon-null-edit' )->parse() |
573 | ) |
574 | ); |
575 | return false; |
576 | } |
577 | |
578 | } |
579 | // Item is updated by createEntryItem(), so we just need to |
580 | // store it. |
581 | $this->modifiedItem = $item; |
582 | |
583 | if ( array_key_exists( 'page', $data ) && $data['page'] ) { |
584 | // @todo Introduce $consumerUrl to request parameters and |
585 | // @todo pass it down here. Currently we're passing null, |
586 | // @todo meaning it only support flushing local wiki |
587 | // @todo utterances. |
588 | $this->purgeOriginPageUtterances( $data['page'], null ); |
589 | } |
590 | |
591 | return true; |
592 | } |
593 | |
594 | /** |
595 | * Immediately removes any utterance from the origin page. |
596 | * @since 0.1.8 |
597 | * @param int $pageId |
598 | * @param string|null $consumerUrl |
599 | */ |
600 | private function purgeOriginPageUtterances( int $pageId, ?string $consumerUrl ) { |
601 | $utteranceStore = new UtteranceStore(); |
602 | $utteranceStore->flushUtterancesByPage( $consumerUrl, $pageId ); |
603 | } |
604 | |
605 | /** |
606 | * Make options to be used by in a select field |
607 | * |
608 | * Each language that is specified in the config variable |
609 | * "WikispeechVoices" is included in the options. The labels are |
610 | * of the format "code - autonym". |
611 | * |
612 | * @since 0.1.8 |
613 | * @return array Keys are labels and values are language codes. |
614 | */ |
615 | private function getLanguageOptions(): array { |
616 | $voices = $this->getConfig()->get( 'WikispeechVoices' ); |
617 | $languages = array_keys( $voices ); |
618 | sort( $languages ); |
619 | $options = []; |
620 | foreach ( $languages as $code ) { |
621 | $name = $this->languageNameUtils->getLanguageName( $code ); |
622 | $label = "$code - $name"; |
623 | $options[$label] = $code; |
624 | } |
625 | ksort( $options ); |
626 | return $options; |
627 | } |
628 | |
629 | /** |
630 | * Show success page containing the properties of the added/edited item |
631 | * |
632 | * @since 0.1.9 |
633 | * @param string $message Success message. |
634 | */ |
635 | private function success( $message ) { |
636 | $this->getOutput()->addHtml( |
637 | Html::successBox( |
638 | $this->msg( $message )->parse() |
639 | ) |
640 | ); |
641 | $this->getOutput()->addHtml( |
642 | Html::element( 'pre', [], $this->modifiedItem ) |
643 | ); |
644 | } |
645 | } |