diff options
Diffstat (limited to 'www/wiki/extensions/Translate/specials/SpecialLanguageStats.php')
-rw-r--r-- | www/wiki/extensions/Translate/specials/SpecialLanguageStats.php | 285 |
1 files changed, 160 insertions, 125 deletions
diff --git a/www/wiki/extensions/Translate/specials/SpecialLanguageStats.php b/www/wiki/extensions/Translate/specials/SpecialLanguageStats.php index 01437dfe..94238370 100644 --- a/www/wiki/extensions/Translate/specials/SpecialLanguageStats.php +++ b/www/wiki/extensions/Translate/specials/SpecialLanguageStats.php @@ -5,7 +5,7 @@ * @file * @author Siebrand Mazeland * @author Niklas Laxström - * @license GPL-2.0+ + * @license GPL-2.0-or-later */ /** @@ -28,7 +28,7 @@ class SpecialLanguageStats extends SpecialPage { /** * @var Array */ - protected $targetValueName = array( 'code', 'language' ); + protected $targetValueName = [ 'code', 'language' ]; /** * Most of the displayed numbers added together at the bottom of the table. @@ -36,13 +36,6 @@ class SpecialLanguageStats extends SpecialPage { protected $totals; /** - * How long spend time calculating missing numbers, before - * bailing out. - * @var int - */ - protected $timelimit = 2; - - /** * Flag to set if nothing to show. * @var bool */ @@ -84,7 +77,7 @@ class SpecialLanguageStats extends SpecialPage { * * @var array */ - protected $statsCounted = array(); + protected $statsCounted = []; /** * @var array @@ -110,6 +103,11 @@ class SpecialLanguageStats extends SpecialPage { $request = $this->getRequest(); $this->purge = $request->getVal( 'action' ) === 'purge'; + if ( $this->purge && !$request->wasPosted() ) { + $this->showPurgeForm(); + return; + } + $this->table = new StatsTable(); $this->setHeaders(); @@ -118,7 +116,7 @@ class SpecialLanguageStats extends SpecialPage { $out = $this->getOutput(); $out->addModules( 'ext.translate.special.languagestats' ); - $out->addModuleStyles( 'ext.translate.messagetable' ); + $out->addModuleStyles( 'ext.translate.statstable' ); $params = explode( '/', $par ); @@ -149,18 +147,44 @@ class SpecialLanguageStats extends SpecialPage { if ( !$this->including() ) { $out->addHelpLink( 'Help:Extension:Translate/Statistics_and_reporting' ); - $out->addHTML( $this->getForm() ); + $this->addForm(); } if ( $this->isValidValue( $this->target ) ) { $this->outputIntroduction(); - $output = $this->getTable(); + + $stats = $this->loadStatistics( $this->target, MessageGroupStats::FLAG_CACHE_ONLY ); + $output = $this->getTable( $stats ); if ( $this->incomplete ) { $out->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-langstats-incomplete' ); } + + if ( $this->incomplete || $this->purge ) { + DeferredUpdates::addCallableUpdate( function () { + // Attempt to recache on the fly the missing stats, unless a + // purge was requested, because that is likely to time out. + // Even though this is executed inside a deferred update, it + // counts towards the maximum execution time limit. If that is + // reached, or any other failure happens, no updates at all + // will be written into the database, as it does only single + // update at the end. Hence we always add a job too, so that + // even the slower updates will get done at some point. In + // regular case (no purge), the job sees that the stats are + // already updated, so it is not much of an overhead. + $jobParams = $this->getCacheRebuildJobParameters( $this->target ); + $jobParams[ 'purge' ] = $this->purge; + $job = MessageGroupStatsRebuildJob::newJob( $jobParams ); + JobQueueGroup::singleton()->push( $job ); + + // $this->purge is only true if request was posted + if ( !$this->purge ) { + $this->loadStatistics( $this->target ); + } + } ); + } if ( $this->nothing ) { $out->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-mgs-nothing' ); } @@ -171,9 +195,23 @@ class SpecialLanguageStats extends SpecialPage { } /** - * Return the list of allowed values for target here. - * @param $value - * @return array + * Get stats. + * @param string $target For which target to get stats + * @param int $flags See MessageGroupStats for possible flags + * @return array[] + */ + protected function loadStatistics( $target, $flags = 0 ) { + return MessageGroupStats::forLanguage( $target, $flags ); + } + + protected function getCacheRebuildJobParameters( $target ) { + return [ 'languagecode' => $target ]; + } + + /** + * Return true if language exist in the list of allowed languages or false otherwise. + * @param string $value + * @return bool */ protected function isValidValue( $value ) { $langs = Language::fetchLanguageNames(); @@ -181,7 +219,9 @@ class SpecialLanguageStats extends SpecialPage { return isset( $langs[$value] ); } - /// Called when the target is unknown. + /** + * Called when the target is unknown. + */ protected function invalidTarget() { $this->getOutput()->wrapWikiMsg( "<div class='error'>$1</div>", @@ -189,79 +229,71 @@ class SpecialLanguageStats extends SpecialPage { ); } + protected function showPurgeForm() { + $formDescriptor[ 'intro' ] = [ + 'type' => 'info', + 'vertical-label' => true, + 'raw' => true, + 'default' => $this->msg( 'confirm-purge-top' )->parse() + ]; + + $context = new DerivativeContext( $this->getContext() ); + $requestValues = $this->getRequest()->getQueryValues(); + + HTMLForm::factory( 'ooui', $formDescriptor, $context ) + ->setWrapperLegendMsg( 'confirm-purge-title' ) + ->setSubmitTextMsg( 'confirm_purge_button' ) + ->addHiddenFields( $requestValues ) + ->show(); + } + /** - * HTML for the top form. - * @return \string HTML - * @todo duplicated code + * HTMLForm for the top form rendering. */ - protected function getForm() { - global $wgScript; - - $out = Html::openElement( 'div' ); - $out .= Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); - $out .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ); - $out .= Html::hidden( 'x', 'D' ); // To detect submission - $out .= Html::openElement( 'fieldset' ); - $out .= Html::element( - 'legend', - array(), - $this->msg( 'translate-language-code' )->text() - ); - $out .= Html::openElement( 'table' ); - - $out .= Html::openElement( 'tr' ); - $out .= Html::openElement( 'td', array( 'class' => 'mw-label' ) ); - $out .= Xml::label( - $this->msg( 'translate-language-code-field-name' )->text(), - 'language' - ); - $out .= Html::closeElement( 'td' ); - $out .= Html::openElement( 'td', array( 'class' => 'mw-input' ) ); - $out .= Xml::input( 'language', 10, $this->target, array( 'id' => 'language' ) ); - $out .= Html::closeElement( 'td' ); - $out .= Html::closeElement( 'tr' ); - - $out .= Html::openElement( 'tr' ); - $out .= Html::openElement( 'td', array( 'colspan' => 2 ) ); - $out .= Xml::checkLabel( - $this->msg( 'translate-suppress-complete' )->text(), - 'suppresscomplete', - 'suppresscomplete', - $this->noComplete - ); - $out .= Html::closeElement( 'td' ); - $out .= Html::closeElement( 'tr' ); - - $out .= Html::openElement( 'tr' ); - $out .= Html::openElement( 'td', array( 'colspan' => 2 ) ); - $out .= Xml::checkLabel( - $this->msg( 'translate-ls-noempty' )->text(), - 'suppressempty', - 'suppressempty', - $this->noEmpty - ); - $out .= Html::closeElement( 'td' ); - $out .= Html::closeElement( 'tr' ); - - $out .= Html::openElement( 'tr' ); - $out .= Html::openElement( 'td', array( 'class' => 'mw-input', 'colspan' => 2 ) ); - $out .= Xml::submitButton( $this->msg( 'translate-ls-submit' )->text() ); - $out .= Html::closeElement( 'td' ); - $out .= Html::closeElement( 'tr' ); + protected function addForm() { + $formDescriptor[ 'language' ] = [ + 'type' => 'text', + 'name' => 'language', + 'id' => 'language', + 'label' => $this->msg( 'translate-language-code-field-name' )->text(), + 'size' => 10, + 'default' => $this->target, + ]; + $formDescriptor[ 'suppresscomplete' ] = [ + 'type' => 'check', + 'label' => $this->msg( 'translate-suppress-complete' )->text(), + 'name' => 'suppresscomplete', + 'id' => 'suppresscomplete', + 'default' => $this->noComplete, + ]; + $formDescriptor[ 'suppressempty' ] = [ + 'type' => 'check', + 'label' => $this->msg( 'translate-ls-noempty' )->text(), + 'name' => 'suppressempty', + 'id' => 'suppressempty', + 'default' => $this->noEmpty, + ]; + + $context = new DerivativeContext( $this->getContext() ); + $context->setTitle( $this->getPageTitle() ); // Remove subpage + + $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $context ); - $out .= Html::closeElement( 'table' ); - $out .= Html::closeElement( 'fieldset' ); /* Since these pages are in the tabgroup with Special:Translate, - * it makes sense to retain the selected group/language parameter - * on post requests even when not relevant to the current page. */ + * it makes sense to retain the selected group/language parameter + * on post requests even when not relevant to the current page. */ $val = $this->getRequest()->getVal( 'group' ); if ( $val !== null ) { - $out .= Html::hidden( 'group', $val ); + $htmlForm->addHiddenField( 'group', $val ); } - $out .= Html::closeElement( 'form' ); - $out .= Html::closeElement( 'div' ); - return $out; + $htmlForm + ->addHiddenField( 'x', 'D' ) // To detect submission + ->setMethod( 'get' ) + ->setSubmitTextMsg( 'translate-ls-submit' ) + ->setWrapperLegendMsg( 'translate-mgs-fieldset' ) + ->prepareForm() + ->displayForm( false ); } /** @@ -273,14 +305,14 @@ class SpecialLanguageStats extends SpecialPage { $this->getLanguage()->getCode() ); - $rcInLangLink = Linker::link( + $rcInLangLink = $this->getLinkRenderer()->makeKnownLink( SpecialPage::getTitleFor( 'Translate', '!recent' ), - $this->msg( 'languagestats-recenttranslations' )->escaped(), - array(), - array( + $this->msg( 'languagestats-recenttranslations' )->text(), + [], + [ 'action' => 'proofread', 'language' => $this->target - ) + ] ); $out = $this->msg( 'languagestats-stats-for', $languageName )->rawParams( $rcInLangLink ) @@ -302,14 +334,19 @@ class SpecialLanguageStats extends SpecialPage { } } + /** + * Returns the value of the workflow state for the given target. + * @param string $target Whose workflow state we want, either the language code or group id + * @return string Workflow state value + */ protected function getWorkflowStateValue( $target ) { - return isset( $this->states[$target] ) ? $this->states[$target] : ''; + return $this->states[$target] ?? ''; } /** * If workflow states are configured, adds a cell with the workflow state to the row, - * @param String $target Whose workflow state do we want, such as language code or group id. - * @param String $state The workflow state id + * @param string $target Whose workflow state do we want, such as language code or group id. + * @param string $state The workflow state id * @return string Html */ protected function getWorkflowStateCell( $target, $state ) { @@ -326,10 +363,16 @@ class SpecialLanguageStats extends SpecialPage { // Same for every language $group = MessageGroups::getGroup( $this->target ); $stateConfig = $group->getMessageGroupStates()->getStates(); + $languageCode = $target; } else { // The message group for this row $group = MessageGroups::getGroup( $target ); $stateConfig = $group->getMessageGroupStates()->getStates(); + $languageCode = $this->target; + } + + if ( $group->getSourceLanguage() === $languageCode ) { + return "\n\t\t" . $this->table->element( '', '', -1 ); } $sortValue = -1; @@ -358,24 +401,19 @@ class SpecialLanguageStats extends SpecialPage { /** * Returns the table itself. - * @return \string HTML + * @param array $stats + * @return string HTML */ - protected function getTable() { + protected function getTable( $stats ) { $table = $this->table; $this->addWorkflowStatesColumn(); $out = ''; - MessageGroupStats::setTimeLimit( $this->timelimit ); - $cache = MessageGroupStats::forLanguage( $this->target ); - - if ( $this->purge ) { - MessageGroupStats::clearLanguage( $this->target ); - } - + TranslateMetadata::preloadGroups( array_keys( MessageGroups::getAllGroups() ) ); $structure = MessageGroups::getGroupStructure(); foreach ( $structure as $item ) { - $out .= $this->makeGroupGroup( $item, $cache ); + $out .= $this->makeGroupGroup( $item, $stats ); } if ( $out ) { @@ -398,12 +436,6 @@ class SpecialLanguageStats extends SpecialPage { return ''; } - - /// @todo Allow extra message here, once total translated volume goes - /// over a certain percentage? (former live hack at translatewiki) - /// if ( $this->totals['2'] && ( $this->totals['1'] / $this->totals['2'] ) > 0.95 ) { - /// $out .= $this->msg( 'translate-somekey' ); - /// } } /** @@ -411,9 +443,9 @@ class SpecialLanguageStats extends SpecialPage { * If $item is an array, meaning that the first group is an * AggregateMessageGroup and the latter are its children, it will recurse * and create rows for them too. - * @param $item Array|MessageGroup - * @param $cache Array Cache as returned by MessageGroupStats::forLanguage - * @param $parent MessageGroup (do not use, used internally only) + * @param MessageGroup|MessageGroup[] $item + * @param array $cache Cache as returned by MessageGroupStats::forLanguage + * @param MessageGroup|null $parent MessageGroup (do not use, used internally only) * @return string */ protected function makeGroupGroup( $item, array $cache, MessageGroup $parent = null ) { @@ -439,7 +471,7 @@ class SpecialLanguageStats extends SpecialPage { * is blacklisted or hidden by filters. * @param MessageGroup $group * @param array $cache - * @param MessageGroup $parent + * @param MessageGroup|null $parent * @return string */ protected function makeGroupRow( MessageGroup $group, array $cache, @@ -464,6 +496,10 @@ class SpecialLanguageStats extends SpecialPage { return ''; } + if ( $total === null ) { + $this->incomplete = true; + } + // Calculation of summary row values if ( !$group instanceof AggregateMessageGroup && !isset( $this->statsCounted[$groupId] ) @@ -474,25 +510,24 @@ class SpecialLanguageStats extends SpecialPage { $state = $this->getWorkflowStateValue( $groupId ); + // Place any state checks like $this->incomplete above this $params = $stats; $params[] = $state; - $params[] = $groupId; + $params[] = md5( $groupId ); $params[] = $this->getLanguage()->getCode(); - $params[] = $this->target; - $cachekey = wfMemcKey( __METHOD__, implode( '-', $params ) ); + $params[] = md5( $this->target ); + $cachekey = wfMemcKey( __METHOD__ . '-v3', implode( '-', $params ) ); $cacheval = wfGetCache( CACHE_ANYTHING )->get( $cachekey ); - if ( !$this->purge && is_string( $cacheval ) ) { + if ( is_string( $cacheval ) ) { return $cacheval; } - $extra = array(); - if ( $total === null ) { - $this->incomplete = true; - } elseif ( $translated === $total ) { - $extra = array( 'action' => 'proofread' ); + $extra = []; + if ( $translated === $total ) { + $extra = [ 'action' => 'proofread' ]; } - $rowParams = array(); + $rowParams = []; $rowParams['data-groupid'] = $groupId; $rowParams['class'] = get_class( $group ); if ( $parent ) { @@ -500,7 +535,7 @@ class SpecialLanguageStats extends SpecialPage { } $out = "\t" . Html::openElement( 'tr', $rowParams ); - $out .= "\n\t\t" . Html::rawElement( 'td', array(), + $out .= "\n\t\t" . Html::rawElement( 'td', [], $this->table->makeGroupLink( $group, $this->target, $extra ) ); $out .= $this->table->makeNumberColumns( $stats ); $out .= $this->getWorkflowStateCell( $groupId, $state ); @@ -512,15 +547,15 @@ class SpecialLanguageStats extends SpecialPage { } protected function getWorkflowStates( $field = 'tgr_group', $filter = 'tgr_lang' ) { - $db = wfGetDB( DB_SLAVE ); + $db = wfGetDB( DB_REPLICA ); $res = $db->select( 'translate_groupreviews', - array( 'tgr_state', $field ), - array( $filter => $this->target ), + [ 'tgr_state', $field ], + [ $filter => $this->target ], __METHOD__ ); - $states = array(); + $states = []; foreach ( $res as $row ) { $states[$row->$field] = $row->tgr_state; } |