diff options
Diffstat (limited to 'MLEB/Translate/src/Statistics/TranslationStatsSpecialPage.php')
-rw-r--r-- | MLEB/Translate/src/Statistics/TranslationStatsSpecialPage.php | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/MLEB/Translate/src/Statistics/TranslationStatsSpecialPage.php b/MLEB/Translate/src/Statistics/TranslationStatsSpecialPage.php new file mode 100644 index 00000000..5b537c9d --- /dev/null +++ b/MLEB/Translate/src/Statistics/TranslationStatsSpecialPage.php @@ -0,0 +1,250 @@ +<?php +declare( strict_types = 1 ); + +namespace MediaWiki\Extension\Translate\Statistics; + +use FormOptions; +use Html; +use JsSelectToInput; +use MessageGroup; +use MessageGroups; +use SpecialPage; +use TranslateUtils; +use Xml; +use XmlSelect; +use function wfEscapeWikiText; + +/** + * Includable special page for generating graphs for statistics. + * + * @file + * @author Niklas Laxström + * @author Siebrand Mazeland + * @license GPL-2.0-or-later + */ +class TranslationStatsSpecialPage extends SpecialPage { + /** @var TranslationStatsDataProvider */ + private $dataProvider; + private const GRAPH_CONTAINER_ID = 'translationStatsGraphContainer'; + private const GRAPH_CONTAINER_CLASS = 'mw-translate-translationstats-container'; + + public function __construct( TranslationStatsDataProvider $dataProvider ) { + parent::__construct( 'TranslationStats' ); + $this->dataProvider = $dataProvider; + } + + /** @inheritDoc */ + public function isIncludable(): bool { + return true; + } + + /** @inheritDoc */ + protected function getGroupName(): string { + return 'translation'; + } + + /** @inheritDoc */ + public function execute( $par ): void { + $graphOpts = new TranslationStatsGraphOptions(); + $graphOpts->bindArray( $this->getRequest()->getValues() ); + + $pars = explode( ';', (string)$par ); + foreach ( $pars as $item ) { + if ( strpos( $item, '=' ) === false ) { + continue; + } + + [ $key, $value ] = array_map( 'trim', explode( '=', $item, 2 ) ); + if ( $graphOpts->hasValue( $key ) ) { + $graphOpts->setValue( $key, $value ); + } + } + + $graphOpts->normalize( $this->dataProvider->getGraphTypes() ); + $opts = $graphOpts->getFormOptions(); + + if ( $this->including() ) { + $this->getOutput()->addHTML( $this->embed( $opts ) ); + } else { + $this->form( $opts ); + } + } + + /** + * Constructs the form which can be used to generate custom graphs. + * + * @suppress SecurityCheck-DoubleEscaped Intentionally outputting what user should type + */ + private function form( FormOptions $opts ): void { + $script = $this->getConfig()->get( 'Script' ); + + $this->setHeaders(); + $out = $this->getOutput(); + $out->addModules( 'ext.translate.special.translationstats' ); + $out->addHelpLink( 'Help:Extension:Translate/Statistics_and_reporting' ); + $out->addWikiMsg( 'translate-statsf-intro' ); + $out->addHTML( + Xml::fieldset( $this->msg( 'translate-statsf-options' )->text() ) . Html::openElement( + 'form', + [ 'action' => $script, 'id' => 'translationStatsConfig' ] + ) . Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) . + Html::hidden( 'preview', 1 ) . '<table>' + ); + $submit = Xml::submitButton( $this->msg( 'translate-statsf-submit' )->text() ); + $out->addHTML( + $this->eInput( 'width', $opts ) . $this->eInput( 'height', $opts ) . + '<tr><td colspan="2"><hr /></td></tr>' . $this->eInput( 'start', $opts, 24 ) . + $this->eInput( 'days', $opts ) . + $this->eRadio( 'scale', $opts, [ 'years', 'months', 'weeks', 'days', 'hours' ] ) . + $this->eRadio( 'count', $opts, $this->dataProvider->getGraphTypes() ) . + '<tr><td colspan="2"><hr /></td></tr>' . $this->eLanguage( 'language', $opts ) . + $this->eGroup( 'group', $opts ) . '<tr><td colspan="2"><hr /></td></tr>' . + '<tr><td colspan="2">' . $submit . '</td></tr>' + ); + $out->addHTML( '</table></form></fieldset>' ); + if ( !$opts['preview'] ) { + return; + } + $spiParams = []; + foreach ( $opts->getChangedValues() as $key => $v ) { + if ( $key === 'preview' ) { + continue; + } + if ( is_array( $v ) ) { + $v = implode( ',', $v ); + if ( !strlen( $v ) ) { + continue; + } + } + $spiParams[] = $key . '=' . wfEscapeWikiText( $v ); + } + if ( $spiParams ) { + $spiParams = '/' . implode( ';', $spiParams ); + } + $titleText = $this->getPageTitle()->getPrefixedText(); + $out->addHTML( Html::element( 'hr' ) ); + // Element to render the graph + $out->addHTML( + Html::rawElement( + 'div', + [ + 'id' => self::GRAPH_CONTAINER_ID, + 'style' => 'margin: 2em auto; display: block', + 'class' => self::GRAPH_CONTAINER_CLASS, + ] + ) + ); + + $out->addHTML( + Html::element( + 'pre', + [ 'aria-label' => $this->msg( 'translate-statsf-embed' )->text() ], + "{{{$titleText}{$spiParams}}}" + ) + ); + } + + /// Construct HTML for a table row with label and input in two columns. + private function eInput( string $name, FormOptions $opts, int $width = 4 ): string { + $value = $opts[$name]; + return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . + Xml::input( $name, $width, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n"; + } + + /// Construct HTML for a label for option. + private function eLabel( string $name ): string { + // Give grep a chance to find the usages: + // translate-statsf-width, translate-statsf-height, translate-statsf-start, + // translate-statsf-days, translate-statsf-scale, translate-statsf-count, + // translate-statsf-language, translate-statsf-group + $label = 'translate-statsf-' . $name; + $label = $this->msg( $label )->escaped(); + return Xml::tags( 'label', [ 'for' => $name ], $label ); + } + + /// Construct HTML for a table row with label and radio input in two columns. + private function eRadio( string $name, FormOptions $opts, array $alts ): string { + // Give grep a chance to find the usages: + // translate-statsf-scale, translate-statsf-count + $label = 'translate-statsf-' . $name; + $label = $this->msg( $label )->escaped(); + $s = '<tr><td>' . $label . '</td><td>'; + $options = []; + foreach ( $alts as $alt ) { + $id = "$name-$alt"; + $radio = Xml::radio( + $name, + $alt, + $alt === $opts[$name], + [ 'id' => $id ] + ) . ' '; + $options[] = $radio . ' ' . $this->eLabel( $id ); + } + $s .= implode( ' ', $options ); + $s .= '</td></tr>' . "\n"; + return $s; + } + + /// Construct HTML for a table row with label and language selector in two columns. + private function eLanguage( string $name, FormOptions $opts ): string { + $value = implode( ',', $opts[$name] ); + + $select = $this->languageSelector(); + $select->setTargetId( 'language' ); + return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() . + '<br />' . Xml::input( $name, 20, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n"; + } + + /// Construct a JavaScript enhanced language selector. + private function languageSelector(): JsSelectToInput { + $languages = TranslateUtils::getLanguageNames( $this->getLanguage()->getCode() ); + ksort( $languages ); + $selector = new XmlSelect( 'mw-language-selector', 'mw-language-selector' ); + foreach ( $languages as $code => $name ) { + $selector->addOption( "$code - $name", $code ); + } + return new JsSelectToInput( $selector ); + } + + /// Constructs HTML for a table row with label and group selector in two columns. + private function eGroup( string $name, FormOptions $opts ): string { + $value = implode( ',', $opts[$name] ); + + $select = $this->groupSelector(); + $select->setTargetId( 'group' ); + return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() . + '<br />' . Xml::input( $name, 20, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n"; + } + + /// Construct a JavaScript enhanced group selector. + private function groupSelector(): JsSelectToInput { + $groups = MessageGroups::singleton()->getGroups(); + /** @var MessageGroup $group */ + foreach ( $groups as $key => $group ) { + if ( !$group->exists() ) { + unset( $groups[$key] ); + } + } + ksort( $groups ); + $selector = new XmlSelect( 'mw-group-selector', 'mw-group-selector' ); + /** @var MessageGroup $name */ + foreach ( $groups as $code => $name ) { + $selector->addOption( $name->getLabel(), $code ); + } + return new JsSelectToInput( $selector ); + } + + private function embed( FormOptions $opts ): string { + $this->getOutput()->addModules( 'ext.translate.translationstats.embedded' ); + return Html::rawElement( + 'div', + [ + 'class' => self::GRAPH_CONTAINER_CLASS, + ], + Html::hidden( + 'translationStatsGraphOptions', + json_encode( $opts->getAllValues() ) + ) + ); + } +} |