Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslationStatsSpecialPage.php
Go to the documentation of this file.
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Statistics;
5
6use FormOptions;
7use Html;
11use SpecialPage;
12use Xml;
13use XmlSelect;
14use function wfEscapeWikiText;
15
24class TranslationStatsSpecialPage extends SpecialPage {
26 private $dataProvider;
27 private const GRAPH_CONTAINER_ID = 'translationStatsGraphContainer';
28 private const GRAPH_CONTAINER_CLASS = 'mw-translate-translationstats-container';
29
30 public function __construct( TranslationStatsDataProvider $dataProvider ) {
31 parent::__construct( 'TranslationStats' );
32 $this->dataProvider = $dataProvider;
33 }
34
36 public function isIncludable(): bool {
37 return true;
38 }
39
41 protected function getGroupName(): string {
42 return 'translation';
43 }
44
46 public function execute( $par ): void {
47 $graphOpts = new TranslationStatsGraphOptions();
48 $graphOpts->bindArray( $this->getRequest()->getValues() );
49
50 $pars = explode( ';', (string)$par );
51 foreach ( $pars as $item ) {
52 if ( !str_contains( $item, '=' ) ) {
53 continue;
54 }
55
56 [ $key, $value ] = array_map( 'trim', explode( '=', $item, 2 ) );
57 if ( $graphOpts->hasValue( $key ) ) {
58 $graphOpts->setValue( $key, $value );
59 }
60 }
61
62 $graphOpts->normalize( $this->dataProvider->getGraphTypes() );
63 $opts = $graphOpts->getFormOptions();
64
65 if ( $this->including() ) {
66 $this->getOutput()->addHTML( $this->embed( $opts ) );
67 } else {
68 $this->form( $opts );
69 }
70 }
71
77 private function form( FormOptions $opts ): void {
78 $script = $this->getConfig()->get( 'Script' );
79
80 $this->setHeaders();
81 $out = $this->getOutput();
82 $out->addModules( 'ext.translate.special.translationstats' );
83 $out->addHelpLink( 'Help:Extension:Translate/Statistics_and_reporting' );
84 $out->addWikiMsg( 'translate-statsf-intro' );
85 $out->addHTML(
86 Xml::fieldset( $this->msg( 'translate-statsf-options' )->text() ) . Html::openElement(
87 'form',
88 [ 'action' => $script, 'id' => 'translationStatsConfig' ]
89 ) . Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
90 Html::hidden( 'preview', 1 ) . '<table>'
91 );
92 $submit = Xml::submitButton( $this->msg( 'translate-statsf-submit' )->text() );
93 $out->addHTML(
94 $this->eInput( 'width', $opts ) . $this->eInput( 'height', $opts ) .
95 '<tr><td colspan="2"><hr /></td></tr>' . $this->eInput( 'start', $opts, 24 ) .
96 $this->eInput( 'days', $opts ) .
97 $this->eRadio( 'scale', $opts, [ 'years', 'months', 'weeks', 'days', 'hours' ] ) .
98 $this->eRadio( 'count', $opts, $this->dataProvider->getGraphTypes() ) .
99 '<tr><td colspan="2"><hr /></td></tr>' . $this->eLanguage( 'language', $opts ) .
100 $this->eGroup( 'group', $opts ) . '<tr><td colspan="2"><hr /></td></tr>' .
101 '<tr><td colspan="2">' . $submit . '</td></tr>'
102 );
103 $out->addHTML( '</table></form></fieldset>' );
104 if ( !$opts['preview'] ) {
105 return;
106 }
107 $spiParams = [];
108 foreach ( $opts->getChangedValues() as $key => $v ) {
109 if ( $key === 'preview' ) {
110 continue;
111 }
112 if ( is_array( $v ) ) {
113 $v = implode( ',', $v );
114 if ( !strlen( $v ) ) {
115 continue;
116 }
117 }
118 $spiParams[] = $key . '=' . wfEscapeWikiText( $v );
119 }
120 if ( $spiParams ) {
121 $spiParams = '/' . implode( ';', $spiParams );
122 }
123 $titleText = $this->getPageTitle()->getPrefixedText();
124 $out->addHTML( Html::element( 'hr' ) );
125 // Element to render the graph
126 $out->addHTML(
127 Html::rawElement(
128 'div',
129 [
130 'id' => self::GRAPH_CONTAINER_ID,
131 'style' => 'margin: 2em auto; display: block',
132 'class' => self::GRAPH_CONTAINER_CLASS,
133 ]
134 )
135 );
136
137 $out->addHTML(
138 Html::element(
139 'pre',
140 [ 'aria-label' => $this->msg( 'translate-statsf-embed' )->text() ],
141 "{{{$titleText}{$spiParams}}}"
142 )
143 );
144 }
145
147 private function eInput( string $name, FormOptions $opts, int $width = 4 ): string {
148 $value = $opts[$name];
149 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' .
150 Xml::input( $name, $width, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n";
151 }
152
154 private function eLabel( string $name ): string {
155 // Give grep a chance to find the usages:
156 // translate-statsf-width, translate-statsf-height, translate-statsf-start,
157 // translate-statsf-days, translate-statsf-scale, translate-statsf-count,
158 // translate-statsf-language, translate-statsf-group
159 $label = 'translate-statsf-' . $name;
160 $label = $this->msg( $label )->escaped();
161 return Xml::tags( 'label', [ 'for' => $name ], $label );
162 }
163
165 private function eRadio( string $name, FormOptions $opts, array $alts ): string {
166 // Give grep a chance to find the usages:
167 // translate-statsf-scale, translate-statsf-count
168 $label = 'translate-statsf-' . $name;
169 $label = $this->msg( $label )->escaped();
170 $s = '<tr><td>' . $label . '</td><td>';
171 $options = [];
172 foreach ( $alts as $alt ) {
173 $id = "$name-$alt";
174 $radio = Xml::radio(
175 $name,
176 $alt,
177 $alt === $opts[$name],
178 [ 'id' => $id ]
179 ) . ' ';
180 $options[] = $radio . ' ' . $this->eLabel( $id );
181 }
182 $s .= implode( ' ', $options );
183 $s .= '</td></tr>' . "\n";
184 return $s;
185 }
186
188 private function eLanguage( string $name, FormOptions $opts ): string {
189 $value = implode( ',', $opts[$name] );
190
191 $select = $this->languageSelector();
192 $select->setTargetId( 'language' );
193 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() .
194 '<br />' . Xml::input( $name, 20, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n";
195 }
196
198 private function languageSelector(): JsSelectToInput {
199 $languages = Utilities::getLanguageNames( $this->getLanguage()->getCode() );
200 ksort( $languages );
201 $selector = new XmlSelect( 'mw-language-selector', 'mw-language-selector' );
202 foreach ( $languages as $code => $name ) {
203 $selector->addOption( "$code - $name", $code );
204 }
205 return new JsSelectToInput( $selector );
206 }
207
209 private function eGroup( string $name, FormOptions $opts ): string {
210 $value = implode( ',', $opts[$name] );
211
212 $select = $this->groupSelector();
213 $select->setTargetId( 'group' );
214 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() .
215 '<br />' . Xml::input( $name, 20, $value, [ 'id' => $name ] ) . '</td></tr>' . "\n";
216 }
217
219 private function groupSelector(): JsSelectToInput {
220 $groups = MessageGroups::singleton()->getGroups();
221 foreach ( $groups as $key => $group ) {
222 if ( !$group->exists() ) {
223 unset( $groups[$key] );
224 }
225 }
226 ksort( $groups );
227 $selector = new XmlSelect( 'mw-group-selector', 'mw-group-selector' );
228 foreach ( $groups as $code => $name ) {
229 $selector->addOption( $name->getLabel(), $code );
230 }
231 return new JsSelectToInput( $selector );
232 }
233
234 private function embed( FormOptions $opts ): string {
235 $this->getOutput()->addModules( 'ext.translate.translationstats.embedded' );
236 return Html::rawElement(
237 'div',
238 [
239 'class' => self::GRAPH_CONTAINER_CLASS,
240 ],
241 Html::hidden(
242 'translationStatsGraphOptions',
243 json_encode( $opts->getAllValues() )
244 )
245 );
246 }
247}
return[ 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->getMainConfig(), $services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'));}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);return new $class( $params);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore($services->getDBLoadBalancerFactory());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'),);}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
Factory class for accessing message groups individually by id or all of them as a list.
Code for JavaScript enhanced <option> selectors.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:31