31 private bool $noComplete =
true;
33 private bool $noEmpty =
false;
35 private string $target;
37 private ?
string $targetType =
null;
38 private ServiceOptions $options;
39 private JobQueueGroup $jobQueueGroup;
44 private const GROUPS =
'group';
45 private const MESSAGES =
'messages';
47 private const CONSTRUCTOR_OPTIONS = [
48 'TranslateMessagePrefixStatsLimit',
51 public function __construct(
53 JobQueueGroup $jobQueueGroup,
58 parent::__construct(
'MessageGroupStats' );
59 $this->options =
new ServiceOptions( self::CONSTRUCTOR_OPTIONS, $config );
60 $this->jobQueueGroup = $jobQueueGroup;
61 $this->messageGroupStatsTableFactory = $messageGroupStatsTableFactory;
62 $this->entitySearch = $entitySearch;
63 $this->messagePrefixStats = $messagePrefixStats;
66 public function getDescription() {
67 return $this->msg(
'translate-mgs-pagename' )->text();
70 public function isIncludable() {
74 protected function getGroupName() {
78 public function execute( $par ) {
79 $request = $this->getRequest();
81 $purge = $request->getVal(
'action' ) ===
'purge';
82 if ( $purge && !$request->wasPosted() ) {
83 LanguageStatsSpecialPage::showPurgeForm( $this->getContext() );
88 $this->outputHeader();
90 $out = $this->getOutput();
92 $out->addModules(
'ext.translate.special.languagestats' );
93 $out->addModuleStyles(
'ext.translate.statstable' );
94 $out->addModuleStyles(
'ext.translate.special.groupstats' );
96 $params = $par ? explode(
'/', $par ) : [];
98 if ( isset( $params[0] ) && trim( $params[0] ) ) {
99 $this->target = $params[0];
102 if ( isset( $params[1] ) ) {
103 $this->noComplete = (bool)$params[1];
106 if ( isset( $params[2] ) ) {
107 $this->noEmpty = (bool)$params[2];
111 $submitted = !$this->including() && $request->getVal(
'x' ) ===
'D';
114 $this->target = $request->getVal( self::GROUPS, $this->target ??
'' );
115 if ( $this->target !==
'' ) {
116 $this->targetType = self::GROUPS;
118 $this->target = $request->getVal( self::MESSAGES,
'' );
119 if ( $this->target !==
'' ) {
120 $this->targetType = self::MESSAGES;
125 $this->noComplete = $request->getBool(
127 $this->noComplete && !$submitted
129 $this->noEmpty = $request->getBool(
'suppressempty', $this->noEmpty && !$submitted );
131 if ( !$this->including() ) {
132 $out->addHelpLink(
'Help:Extension:Translate/Statistics_and_reporting' );
136 $stats = $output =
null;
137 if ( $this->targetType === self::GROUPS && $this->isValidGroup( $this->target ) ) {
138 $this->outputIntroduction();
140 $stats = $this->loadStatistics( $this->target, MessageGroupStats::FLAG_CACHE_ONLY );
142 $messageGroupStatsTable = $this->messageGroupStatsTableFactory->newFromContext( $this->getContext() );
143 $output = $messageGroupStatsTable->get(
145 MessageGroups::getGroup( $this->target ),
150 $incomplete = $messageGroupStatsTable->areStatsIncomplete();
153 "<div class='error'>$1</div>",
154 'translate-langstats-incomplete'
158 if ( $incomplete || $purge ) {
159 DeferredUpdates::addCallableUpdate(
function () use ( $purge ) {
170 $jobParams = $this->getCacheRebuildJobParameters( $this->target );
171 $jobParams[
'purge' ] = $purge;
172 $job = MessageGroupStatsRebuildJob::newJob( $jobParams );
173 $this->jobQueueGroup->push( $job );
177 $this->loadStatistics( $this->target );
181 } elseif ( $this->targetType === self::MESSAGES ) {
182 $messagesWithPrefix = $this->entitySearch->matchMessages( $this->target );
183 if ( $messagesWithPrefix ) {
184 $messageWithPrefixLimit = $this->options->get(
'TranslateMessagePrefixStatsLimit' );
185 if ( count( $messagesWithPrefix ) > $messageWithPrefixLimit ) {
188 $this->msg(
'translate-mgs-message-prefix-limit' )
189 ->params( $messageWithPrefixLimit )
196 $stats = $this->messagePrefixStats->forAll( ...$messagesWithPrefix );
197 $messageGroupStatsTable = $this->messageGroupStatsTableFactory
198 ->newFromContext( $this->getContext() );
199 $output = $messageGroupStatsTable->get(
210 $out->addHTML( $output );
211 } elseif ( $stats !==
null ) {
213 $out->addHTML( Html::warningBox( $this->msg(
'translate-mgs-nothing' )->parse() ) );
214 } elseif ( $submitted ) {
215 $this->invalidTarget();
219 private function loadStatistics(
string $target,
int $flags = 0 ): array {
220 return MessageGroupStats::forGroup( $target, $flags );
223 private function getCacheRebuildJobParameters(
string $target ): array {
224 return [
'groupid' => $target ];
227 private function isValidGroup( ?
string $value ):
bool {
228 if ( $value ===
null ) {
232 $group = MessageGroups::getGroup( $value );
234 if ( MessageGroups::isDynamic( $group ) ) {
242 $this->target = $group->getId();
249 private function invalidTarget():
void {
250 $this->getOutput()->wrapWikiMsg(
251 "<div class='error'>$1</div>",
252 [
'translate-mgs-invalid-group', $this->target ]
256 private function outputIntroduction():
void {
257 $priorityLangs = TranslateMetadata::get( $this->target,
'prioritylangs' );
258 if ( $priorityLangs ) {
259 $hasPriorityForce = TranslateMetadata::get( $this->target,
'priorityforce' ) ===
'on';
260 if ( $hasPriorityForce ) {
261 $this->getOutput()->addWikiMsg(
'tpt-priority-languages-force', $priorityLangs );
263 $this->getOutput()->addWikiMsg(
'tpt-priority-languages', $priorityLangs );
268 private function addForm():
void {
272 'name' => self::GROUPS,
273 'id' => self::GROUPS,
274 'label' => $this->msg(
'translate-mgs-group' )->text(),
275 'options' => $this->getGroupOptions(),
276 'default' => $this->targetType === self::GROUPS ? $this->target :
null,
277 'cssclass' =>
'message-group-selector'
281 'name' => self::MESSAGES,
282 'id' => self::MESSAGES,
283 'label' => $this->msg(
'translate-mgs-prefix' )->text(),
284 'default' => $this->targetType === self::MESSAGES ? $this->target :
null,
285 'cssclass' =>
'message-prefix-selector'
287 'nocomplete-check' => [
289 'name' =>
'suppresscomplete',
290 'id' =>
'suppresscomplete',
291 'label' => $this->msg(
'translate-mgs-nocomplete' )->text(),
292 'default' => $this->noComplete,
296 'name' =>
'suppressempty',
297 'id' =>
'suppressempty',
298 'label' => $this->msg(
'translate-mgs-noempty' )->text(),
299 'default' => $this->noEmpty,
303 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->getContext() );
308 $val = $this->getRequest()->getVal(
'language' );
309 if ( $val !==
null ) {
310 $htmlForm->addHiddenField(
'language', $val );
314 ->addHiddenField(
'x',
'D' )
316 ->setId(
'mw-message-group-stats-form' )
317 ->setSubmitTextMsg(
'translate-mgs-submit' )
318 ->setWrapperLegendMsg(
'translate-mgs-fieldset' )
320 ->displayForm(
false );
324 private function getGroupOptions(): array {
325 $options = [
'' => null ];
326 $groups = MessageGroups::getAllGroups();
328 foreach ( $groups as $id => $class ) {
329 if ( MessageGroups::getGroup( $id )->exists() ) {
330 $options[$class->getLabel()] = $id;