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 DateTime;
7use DateTimeZone;
11use MediaWiki\Html\FormOptions;
12use MediaWiki\Html\Html;
13use MediaWiki\SpecialPage\SpecialPage;
14use MediaWiki\Xml\Xml;
15use MediaWiki\Xml\XmlSelect;
16use function wfEscapeWikiText;
17
26class TranslationStatsSpecialPage extends SpecialPage {
27 private TranslationStatsDataProvider $dataProvider;
28 private const GRAPH_CONTAINER_ID = 'translationStatsGraphContainer';
29 private const GRAPH_CONTAINER_CLASS = 'mw-translate-translationstats-container';
30
31 public function __construct( TranslationStatsDataProvider $dataProvider ) {
32 parent::__construct( 'TranslationStats' );
33 $this->dataProvider = $dataProvider;
34 }
35
37 public function isIncludable(): bool {
38 return true;
39 }
40
42 protected function getGroupName(): string {
43 return 'translation';
44 }
45
47 public function execute( $par ): void {
48 $graphOpts = new TranslationStatsGraphOptions();
49 $graphOpts->bindArray( $this->getRequest()->getValues() );
50
51 $pars = explode( ';', (string)$par );
52 foreach ( $pars as $item ) {
53 if ( !str_contains( $item, '=' ) ) {
54 continue;
55 }
56
57 [ $key, $value ] = array_map( 'trim', explode( '=', $item, 2 ) );
58 if ( $graphOpts->hasValue( $key ) ) {
59 $graphOpts->setValue( $key, $value );
60 }
61 }
62
63 $graphOpts->normalize( $this->dataProvider->getGraphTypes() );
64 $opts = $graphOpts->getFormOptions();
65
66 if ( $this->including() ) {
67 $this->getOutput()->addHTML( $this->embed( $opts ) );
68 } else {
69 $this->form( $opts );
70 }
71 }
72
78 private function form( FormOptions $opts ): void {
79 $script = $this->getConfig()->get( 'Script' );
80
81 $this->setHeaders();
82 $out = $this->getOutput();
83 $out->addModules( 'ext.translate.special.translationstats' );
84 $out->addHelpLink( 'Help:Extension:Translate/Statistics_and_reporting' );
85 $out->addWikiMsg( 'translate-statsf-intro' );
86 $out->addHTML(
87 Html::openElement( 'fieldset' ) .
88 Html::element( 'legend', null, $this->msg( 'translate-statsf-options' )->text() ) .
89 Html::openElement(
90 'form',
91 [ 'action' => $script, 'id' => 'translationStatsConfig' ]
92 ) .
93 Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
94 Html::hidden( 'preview', 1 ) . '<table>'
95 );
96 $now = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
97 $submit = Html::submitButton( $this->msg( 'translate-statsf-submit' )->text() );
98 $out->addHTML(
99 $this->eNumber( 'width', $opts ) . $this->eNumber( 'height', $opts ) .
100 '<tr><td colspan="2"><hr /></td></tr>' .
101 $this->eInput( 'start', $opts, 'date', [ 'max' => $now->format( 'Y-m-d' ) ] ) .
102 $this->eNumber( 'days', $opts ) .
103 $this->eRadio( 'scale', $opts, [ 'years', 'months', 'weeks', 'days', 'hours' ] ) .
104 $this->eRadio( 'count', $opts, $this->dataProvider->getGraphTypes() ) .
105 '<tr><td colspan="2"><hr /></td></tr>' . $this->eLanguage( 'language', $opts ) .
106 $this->eGroup( 'group', $opts ) . '<tr><td colspan="2"><hr /></td></tr>' .
107 '<tr><td colspan="2">' . $submit . '</td></tr>'
108 );
109 $out->addHTML( '</table></form></fieldset>' );
110 if ( !$opts['preview'] ) {
111 return;
112 }
113 $spiParams = [];
114 foreach ( $opts->getChangedValues() as $key => $v ) {
115 if ( $key === 'preview' ) {
116 continue;
117 }
118 if ( is_array( $v ) ) {
119 $v = implode( ',', $v );
120 if ( !strlen( $v ) ) {
121 continue;
122 }
123 }
124 $spiParams[] = $key . '=' . wfEscapeWikiText( $v );
125 }
126
127 $spiParams = $spiParams ? '/' . implode( ';', $spiParams ) : '';
128
129 $titleText = $this->getPageTitle()->getPrefixedText();
130 $out->addHTML( Html::element( 'hr' ) );
131 // Element to render the graph
132 $out->addHTML(
133 Html::rawElement(
134 'div',
135 [
136 'id' => self::GRAPH_CONTAINER_ID,
137 'style' => 'margin: 2em auto; display: block',
138 'class' => self::GRAPH_CONTAINER_CLASS,
139 ]
140 )
141 );
142
143 $out->addHTML(
144 Html::element(
145 'pre',
146 [ 'aria-label' => $this->msg( 'translate-statsf-embed' )->text() ],
147 "{{{$titleText}{$spiParams}}}"
148 )
149 );
150 }
151
153 private function eNumber( string $name, FormOptions $opts ): string {
154 return $this->eInput( $name, $opts, 'number', TranslationStatsGraphOptions::INT_BOUNDS[$name] );
155 }
156
158 private function eInput( string $name, FormOptions $opts, string $type, array $attribs ): string {
159 $value = $opts[$name];
160 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' .
161 Html::input( $name, $value, $type, [ 'id' => $name ] + $attribs ) .
162 '</td></tr>' . "\n";
163 }
164
166 private function eLabel( string $name ): string {
167 // Give grep a chance to find the usages:
168 // translate-statsf-width, translate-statsf-height, translate-statsf-start,
169 // translate-statsf-days, translate-statsf-scale, translate-statsf-count,
170 // translate-statsf-language, translate-statsf-group
171 $label = 'translate-statsf-' . $name;
172 $label = $this->msg( $label )->escaped();
173 return Xml::tags( 'label', [ 'for' => $name ], $label );
174 }
175
177 private function eRadio( string $name, FormOptions $opts, array $alts ): string {
178 // Give grep a chance to find the usages:
179 // translate-statsf-scale, translate-statsf-count
180 $label = 'translate-statsf-' . $name;
181 $label = $this->msg( $label )->escaped();
182 $s = '<tr><td>' . $label . '</td><td>';
183 $options = [];
184 foreach ( $alts as $alt ) {
185 $id = "$name-$alt";
186 $radio = Html::radio(
187 $name,
188 $alt === $opts[$name],
189 [
190 'value' => $alt,
191 'id' => $id
192 ]
193 ) . ' ';
194 $options[] = $radio . ' ' . $this->eLabel( $id );
195 }
196 $s .= implode( ' ', $options );
197 $s .= '</td></tr>' . "\n";
198 return $s;
199 }
200
202 private function eLanguage( string $name, FormOptions $opts ): string {
203 $value = implode( ',', $opts[$name] );
204
205 $select = $this->languageSelector();
206 $select->setTargetId( 'language' );
207 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() .
208 '<br />' . Html::input( $name, $value, 'text', [ 'id' => $name, 'size' => 20 ] ) .
209 '</td></tr>' . "\n";
210 }
211
213 private function languageSelector(): JsSelectToInput {
214 $languages = Utilities::getLanguageNames( $this->getLanguage()->getCode() );
215 ksort( $languages );
216 $selector = new XmlSelect( 'mw-language-selector', 'mw-language-selector' );
217 foreach ( $languages as $code => $name ) {
218 $selector->addOption( "$code - $name", $code );
219 }
220 return new JsSelectToInput( $selector );
221 }
222
224 private function eGroup( string $name, FormOptions $opts ): string {
225 $value = implode( ',', $opts[$name] );
226
227 $select = $this->groupSelector();
228 $select->setTargetId( 'group' );
229 return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . $select->getHtmlAndPrepareJS() .
230 '<br />' . Html::input( $name, $value, 'text', [ 'id' => $name, 'size' => 20 ] ) .
231 '</td></tr>' . "\n";
232 }
233
235 private function groupSelector(): JsSelectToInput {
236 $groups = MessageGroups::singleton()->getGroups();
237 foreach ( $groups as $key => $group ) {
238 if ( !$group->exists() ) {
239 unset( $groups[$key] );
240 }
241 }
242 ksort( $groups );
243 $selector = new XmlSelect( 'mw-group-selector', 'mw-group-selector' );
244 foreach ( $groups as $code => $name ) {
245 $selector->addOption( $name->getLabel(), $code );
246 }
247 return new JsSelectToInput( $selector );
248 }
249
250 private function embed( FormOptions $opts ): string {
251 $this->getOutput()->addModules( 'ext.translate.translationstats.embedded' );
252 return Html::rawElement(
253 'div',
254 [
255 'class' => self::GRAPH_CONTAINER_CLASS,
256 ],
257 Html::hidden(
258 'translationStatsGraphOptions',
259 json_encode( $opts->getAllValues() )
260 )
261 );
262 }
263}
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager($services->getTitleFactory(), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, '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:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance(LogNames::GROUP_SYNCHRONIZATION), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, '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:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleDependencyPurger'=> static function(MediaWikiServices $services):MessageBundleDependencyPurger { return new MessageBundleDependencyPurger( $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getConnectionProvider());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getConnectionProvider(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance(LogNames::GROUP_SUBSCRIPTION), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getConnectionProvider());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance(LogNames::MAIN), $services->getMainObjectStash(), $services->getConnectionProvider(), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, '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->getConnectionProvider(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getConnectionProvider());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getConnectionProvider());}, '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(), $services->getNamespaceInfo(), $services->getTitleFactory(), $services->getFormatterFactory());}, '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->getConnectionProvider(), $services->getObjectCacheFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getConnectionProvider() ->getPrimaryDatabase(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getConnectionProvider(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'), $services->get( 'Translate:MessageGroupSubscription'), $services->getFormatterFactory());}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getConnectionProvider(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getConnectionProvider(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getConnectionProvider(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { return new TranslationStashStorage( $services->getConnectionProvider() ->getPrimaryDatabase());}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getConnectionProvider());}, '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