diff options
Diffstat (limited to 'MLEB/Translate/specials')
-rw-r--r-- | MLEB/Translate/specials/SpecialAggregateGroups.php | 287 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialExportTranslations.php | 5 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialImportTranslations.php | 3 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialLanguageStats.php | 20 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialManageGroups.php | 110 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialMessageGroupStats.php | 2 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialSearchTranslations.php | 3 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialTranslate.php | 4 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialTranslationStats.php | 298 | ||||
-rw-r--r-- | MLEB/Translate/specials/SpecialTranslations.php | 7 |
10 files changed, 109 insertions, 630 deletions
diff --git a/MLEB/Translate/specials/SpecialAggregateGroups.php b/MLEB/Translate/specials/SpecialAggregateGroups.php deleted file mode 100644 index afc39032..00000000 --- a/MLEB/Translate/specials/SpecialAggregateGroups.php +++ /dev/null @@ -1,287 +0,0 @@ -<?php -/** - * Contains logic for special page Special:AggregateGroups. - * - * @file - * @author Santhosh Thottingal - * @author Niklas Laxström - * @author Siebrand Mazeland - * @author Kunal Grover - * @license GPL-2.0-or-later - */ - -class SpecialAggregateGroups extends SpecialPage { - protected $hasPermission = false; - - public function __construct() { - parent::__construct( 'AggregateGroups', 'translate-manage' ); - } - - protected function getGroupName() { - return 'translation'; - } - - public function execute( $parameters ) { - $this->setHeaders(); - $this->addHelpLink( 'Help:Extension:Translate/Page translation administration' ); - - $out = $this->getOutput(); - $out->addModuleStyles( 'ext.translate.specialpages.styles' ); - - // Check permissions - if ( $this->getUser()->isAllowed( 'translate-manage' ) ) { - $this->hasPermission = true; - } - - $groupsPreload = array_merge( - MessageGroups::getGroupsByType( WikiPageMessageGroup::class ), - MessageGroups::getGroupsByType( AggregateMessageGroup::class ) - ); - TranslateMetadata::preloadGroups( array_keys( $groupsPreload ) ); - - $groups = MessageGroups::getAllGroups(); - uasort( $groups, [ MessageGroups::class, 'groupLabelSort' ] ); - $aggregates = []; - $pages = []; - foreach ( $groups as $group ) { - if ( $group instanceof WikiPageMessageGroup ) { - $pages[] = $group; - } elseif ( $group instanceof AggregateMessageGroup ) { - $subgroups = TranslateMetadata::getSubgroups( $group->getId() ); - if ( $subgroups !== null ) { - $aggregates[] = $group; - } - } - } - - if ( !count( $pages ) ) { - // @todo Use different message - $out->addWikiMsg( 'tpt-list-nopages' ); - - return; - } - - $this->showAggregateGroups( $aggregates ); - } - - /** - * @param AggregateMessageGroup $group - * @return string - */ - protected function showAggregateGroup( $group ) { - $out = ''; - $id = $group->getId(); - $label = $group->getLabel(); - $desc = $group->getDescription( $this->getContext() ); - - $div = Html::openElement( 'div', [ - 'class' => 'mw-tpa-group', - 'data-groupid' => $id, - 'data-id' => $this->htmlIdForGroup( $group ), - ] ); - - $out .= $div; - - $edit = ''; - $remove = ''; - $editGroup = ''; - $select = ''; - $addButton = ''; - - // Add divs for editing Aggregate Groups - if ( $this->hasPermission ) { - // Group edit and remove buttons - $edit = Html::element( 'span', [ 'class' => 'tp-aggregate-edit-ag-button' ] ); - $remove = Html::element( 'span', [ 'class' => 'tp-aggregate-remove-ag-button' ] ); - - // Edit group div - $editGroupNameLabel = $this->msg( 'tpt-aggregategroup-edit-name' )->escaped(); - $editGroupName = Html::input( - 'tp-agg-name', - $label, - 'text', - [ 'class' => 'tp-aggregategroup-edit-name', 'maxlength' => '200' ] - ); - $editGroupDescriptionLabel = $this->msg( 'tpt-aggregategroup-edit-description' )->escaped(); - $editGroupDescription = Html::input( - 'tp-agg-desc', - $desc, - 'text', - [ 'class' => 'tp-aggregategroup-edit-description' ] - ); - $saveButton = Xml::submitButton( - $this->msg( 'tpt-aggregategroup-update' )->text(), - [ 'class' => 'tp-aggregategroup-update' ] - ); - $cancelButton = Xml::submitButton( - $this->msg( 'tpt-aggregategroup-update-cancel' )->text(), - [ 'class' => 'tp-aggregategroup-update-cancel' ] - ); - $editGroup = Html::rawElement( - 'div', - [ - 'class' => 'tp-edit-group hidden' - ], - $editGroupNameLabel . - $editGroupName . '<br />' . - $editGroupDescriptionLabel . - $editGroupDescription . - $saveButton . - $cancelButton - ); - - // Subgroups selector - $select = Html::input( - 'tp-subgroups-input', - '', - 'text', - [ 'class' => 'tp-group-input' ] - ); - $addButton = Html::element( 'input', - [ 'type' => 'button', - 'value' => $this->msg( 'tpt-aggregategroup-add' )->text(), - 'class' => 'tp-aggregate-add-button' ] - ); - } - - // Aggregate Group info div - $groupName = Html::rawElement( 'h2', - [ 'class' => 'tp-name' ], - htmlspecialchars( $label ) . $edit . $remove - ); - $groupDesc = Html::element( 'p', - [ 'class' => 'tp-desc' ], - $desc - ); - $groupInfo = Html::rawElement( 'div', - [ 'class' => 'tp-display-group' ], - $groupName . - $groupDesc - ); - - $out .= $groupInfo; - $out .= $editGroup; - $out .= $this->listSubgroups( $group ); - $out .= $select . $addButton; - $out .= '</div>'; - - return $out; - } - - /** @param array $aggregates */ - protected function showAggregateGroups( array $aggregates ) { - $out = $this->getOutput(); - $out->addModules( 'ext.translate.special.aggregategroups' ); - - $nojs = Html::element( - 'div', - [ 'class' => 'tux-nojs errorbox' ], - $this->msg( 'tux-nojs' )->plain() - ); - - $out->addHTML( $nojs ); - - /** @var AggregateMessageGroup $group */ - foreach ( $aggregates as $group ) { - $out->addHTML( $this->showAggregateGroup( $group ) ); - } - - // Add new group if user has permissions - if ( $this->hasPermission ) { - $out->addHTML( "<br/><a class='tpt-add-new-group' href='#'>" . - $this->msg( 'tpt-aggregategroup-add-new' )->escaped() . - '</a>' ); - $newGroupNameLabel = $this->msg( 'tpt-aggregategroup-new-name' )->escaped(); - $newGroupName = Html::element( - 'input', - [ 'class' => 'tp-aggregategroup-add-name', 'maxlength' => '200' ] - ); - $newGroupDescriptionLabel = $this->msg( 'tpt-aggregategroup-new-description' )->escaped(); - $newGroupDescription = Html::element( 'input', - [ 'class' => 'tp-aggregategroup-add-description' ] - ); - $saveButton = Html::element( 'input', [ - 'type' => 'button', - 'value' => $this->msg( 'tpt-aggregategroup-save' )->text(), - 'id' => 'tpt-aggregategroups-save', - 'class' => 'tp-aggregate-save-button' - ] ); - $newGroupDiv = Html::rawElement( - 'div', - [ 'class' => 'tpt-add-new-group hidden' ], - "$newGroupNameLabel $newGroupName<br />" . - "$newGroupDescriptionLabel $newGroupDescription<br />$saveButton" - ); - $out->addHTML( $newGroupDiv ); - } - } - - /** - * @param AggregateMessageGroup $parent - * @return string - */ - protected function listSubgroups( AggregateMessageGroup $parent ) { - $id = $this->htmlIdForGroup( $parent, 'mw-tpa-grouplist-' ); - $out = Html::openElement( 'ol', [ 'id' => $id ] ); - - // Not calling $parent->getGroups() because it has done filtering already - $subgroupIds = TranslateMetadata::getSubgroups( $parent->getId() ) ?? []; - - // Get the respective groups and sort them - $subgroups = MessageGroups::getGroupsById( $subgroupIds ); - '@phan-var WikiPageMessageGroup[] $subgroups'; - uasort( $subgroups, [ MessageGroups::class, 'groupLabelSort' ] ); - - // Avoid potentially thousands of separate database queries - $lb = new LinkBatch(); - foreach ( $subgroups as $group ) { - $lb->addObj( $group->getTitle() ); - } - $lb->setCaller( __METHOD__ ); - $lb->execute(); - - // Add missing invalid group ids back, not returned by getGroupsById - foreach ( $subgroupIds as $id ) { - if ( !isset( $subgroups[$id] ) ) { - $subgroups[$id] = null; - } - } - - foreach ( $subgroups as $id => $group ) { - $remove = ''; - if ( $this->hasPermission ) { - $remove = Html::element( 'span', - [ - 'class' => 'tp-aggregate-remove-button', - 'data-groupid' => $id, - ] - ); - } - - if ( $group ) { - $text = $this->getLinkRenderer()->makeKnownLink( $group->getTitle() ); - $note = MessageGroups::getPriority( $id ); - } else { - $text = htmlspecialchars( $id ); - $note = $this->msg( 'tpt-aggregategroup-invalid-group' )->escaped(); - } - - $out .= Html::rawElement( 'li', [], "$text$remove $note" ); - } - $out .= Html::closeElement( 'ol' ); - - return $out; - } - - /** - * @param MessageGroup $group - * @param string $prefix - * @return string - */ - protected function htmlIdForGroup( MessageGroup $group, $prefix = '' ) { - $id = sha1( $group->getId() ); - $id = substr( $id, 5, 8 ); - - return $prefix . $id; - } -} diff --git a/MLEB/Translate/specials/SpecialExportTranslations.php b/MLEB/Translate/specials/SpecialExportTranslations.php index 2e39f3c0..15add963 100644 --- a/MLEB/Translate/specials/SpecialExportTranslations.php +++ b/MLEB/Translate/specials/SpecialExportTranslations.php @@ -189,7 +189,10 @@ class SpecialExportTranslations extends SpecialPage { } if ( !$ffs instanceof GettextFFS ) { - $group = FileBasedMessageGroup::newFromMessageGroup( $group ); + if ( !$group instanceof FileBasedMessageGroup ) { + $group = FileBasedMessageGroup::newFromMessageGroup( $group ); + } + $ffs = new GettextFFS( $group ); } diff --git a/MLEB/Translate/specials/SpecialImportTranslations.php b/MLEB/Translate/specials/SpecialImportTranslations.php index eea01221..a3b472d0 100644 --- a/MLEB/Translate/specials/SpecialImportTranslations.php +++ b/MLEB/Translate/specials/SpecialImportTranslations.php @@ -41,8 +41,6 @@ class SpecialImportTranslations extends SpecialPage { // Security and validity checks if ( !$this->userCanExecute( $this->getUser() ) ) { $this->displayRestrictionError(); - - return; } if ( !$this->getRequest()->wasPosted() ) { @@ -98,6 +96,7 @@ class SpecialImportTranslations extends SpecialPage { } $importer = new MessageWebImporter( $this->getPageTitle(), $group, $code ); + $importer->setUser( $this->getUser() ); $alldone = $importer->execute( $messages ); if ( $alldone ) { diff --git a/MLEB/Translate/specials/SpecialLanguageStats.php b/MLEB/Translate/specials/SpecialLanguageStats.php index 62aa3f79..4e18239a 100644 --- a/MLEB/Translate/specials/SpecialLanguageStats.php +++ b/MLEB/Translate/specials/SpecialLanguageStats.php @@ -8,6 +8,8 @@ * @license GPL-2.0-or-later */ +use MediaWiki\Cache\LinkBatchFactory; + /** * Implements includable special page Special:LanguageStats which provides * translation statistics for all defined message groups. @@ -67,12 +69,15 @@ class SpecialLanguageStats extends SpecialPage { protected $statsCounted = []; /** @var array */ protected $states; + /** @var LinkBatchFactory */ + private $linkBatchFactory; - public function __construct() { + public function __construct( LinkBatchFactory $linkBatchFactory ) { parent::__construct( 'LanguageStats' ); $this->target = $this->getLanguage()->getCode(); $this->totals = MessageGroupStats::getEmptyStats(); + $this->linkBatchFactory = $linkBatchFactory; } public function isIncludable() { @@ -398,7 +403,16 @@ class SpecialLanguageStats extends SpecialPage { $this->addWorkflowStatesColumn(); $out = ''; - TranslateMetadata::preloadGroups( array_keys( MessageGroups::getAllGroups() ) ); + // This avoids a database query per translatable page, which would be caused by + // $group->getSourceLanguage() in $this->getWorkflowStateCell without preloading + $lb = $this->linkBatchFactory->newLinkBatch(); + foreach ( MessageGroups::getAllGroups() as $group ) { + if ( $group instanceof WikiPageMessageGroup ) { + $lb->addObj( $group->getTitle() ); + } + } + $lb->setCaller( __METHOD__ )->execute(); + $structure = MessageGroups::getGroupStructure(); foreach ( $structure as $item ) { $out .= $this->makeGroupGroup( $item, $stats ); @@ -467,7 +481,7 @@ class SpecialLanguageStats extends SpecialPage { ) { $groupId = $group->getId(); - if ( $this->table->isExcluded( $groupId, $this->target ) !== null ) { + if ( $this->table->isExcluded( $groupId, $this->target ) ) { return ''; } diff --git a/MLEB/Translate/specials/SpecialManageGroups.php b/MLEB/Translate/specials/SpecialManageGroups.php index 5db58fa2..cec2b8ce 100644 --- a/MLEB/Translate/specials/SpecialManageGroups.php +++ b/MLEB/Translate/specials/SpecialManageGroups.php @@ -5,6 +5,7 @@ use MediaWiki\Extension\Translate\MessageSync\MessageSourceChange; use MediaWiki\Extension\Translate\Synchronization\DisplayGroupSynchronizationInfo; use MediaWiki\Extension\Translate\Synchronization\GroupSynchronizationCache; use MediaWiki\Extension\Translate\Synchronization\MessageUpdateParameter; +use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; use MediaWiki\Revision\RevisionLookup; use MediaWiki\Revision\SlotRecord; @@ -170,7 +171,8 @@ class SpecialManageGroups extends SpecialPage { // The above count as three $limit -= 3; - if ( $this->getConfig()->get( 'TranslateGroupSynchronizationCache' ) ) { + $groupSyncCacheEnabled = $this->getConfig()->get( 'TranslateGroupSynchronizationCache' ); + if ( $groupSyncCacheEnabled ) { $out->addHTML( $this->displayGroupSyncInfo->getGroupsInSyncHtml( $this->synchronizationCache->getGroupsInSync(), @@ -195,6 +197,12 @@ class SpecialManageGroups extends SpecialPage { ); $out->addHTML( Html::element( 'h2', [], $group->getLabel() ) ); + if ( $groupSyncCacheEnabled && $this->synchronizationCache->groupHasErrors( $id ) ) { + $out->addHTML( + Html::warningBox( $this->msg( 'translate-smg-group-sync-error-warn' )->escaped(), 'center' ) + ); + } + // Reduce page existance queries to one per group $lb = new LinkBatch(); $ns = $group->getNamespace(); @@ -267,6 +275,7 @@ class SpecialManageGroups extends SpecialPage { $title = Title::makeTitleSafe( $group->getNamespace(), "$key/$language" ); $id = self::changeId( $group->getId(), $language, $type, $key ); $noticeHtml = ''; + $isReusedKey = false; if ( $title && $type === 'addition' && $title->exists() ) { // The message has for some reason dropped out from cache @@ -278,6 +287,7 @@ class SpecialManageGroups extends SpecialPage { // leads to many other annoying problems. $type = 'change'; $noticeHtml .= Html::warningBox( $this->msg( 'translate-manage-key-reused' )->text() ); + $isReusedKey = true; } elseif ( $title && ( $type === 'deletion' || $type === 'change' ) && !$title->exists() ) { // This happens if a message key has been renamed // The change can be ignored. @@ -288,11 +298,10 @@ class SpecialManageGroups extends SpecialPage { $titleLink = $this->getLinkRenderer()->makeLink( $title ); if ( $type === 'deletion' ) { - $wiki = ContentHandler::getContentText( - $this->revLookup - ->getRevisionByTitle( $title ) - ->getContent( SlotRecord::MAIN ) - ); + $content = $this->revLookup + ->getRevisionByTitle( $title ) + ->getContent( SlotRecord::MAIN ); + $wiki = ( $content instanceof TextContent ) ? $content->getText() : ''; if ( $wiki === '' ) { $noticeHtml .= Html::warningBox( @@ -348,18 +357,35 @@ class SpecialManageGroups extends SpecialPage { $actions = ''; $importSelected = true; - if ( $group->getSourceLanguage() === $language ) { + $sourceLanguage = $group->getSourceLanguage(); + + if ( $sourceLanguage === $language ) { $importSelected = false; $label = $this->msg( 'translate-manage-action-fuzzy' )->text(); $actions .= Xml::radioLabel( $label, "msg/$id", "fuzzy", "f/$id", true ); } - $label = $this->msg( 'translate-manage-action-import' )->text(); - $actions .= Xml::radioLabel( $label, "msg/$id", "import", "imp/$id", $importSelected ); + if ( + $sourceLanguage !== $language && + $isReusedKey && + !self::isMessageDefinitionPresent( $group, $changes, $key ) + ) { + $noticeHtml .= Html::warningBox( + $this->msg( 'translate-manage-source-message-not-found' )->text(), + 'mw-translate-smg-notice-important' + ); - $label = $this->msg( 'translate-manage-action-ignore' )->text(); - $actions .= Xml::radioLabel( $label, "msg/$id", "ignore", "i/$id" ); - $limit--; + // Automatically ignore messages that don't have a definitions + $actions .= Html::hidden( "msg/$id", 'ignore', [ 'id' => "i/$id" ] ); + $limit--; + } else { + $label = $this->msg( 'translate-manage-action-import' )->text(); + $actions .= Xml::radioLabel( $label, "msg/$id", "import", "imp/$id", $importSelected ); + + $label = $this->msg( 'translate-manage-action-ignore' )->text(); + $actions .= Xml::radioLabel( $label, "msg/$id", "ignore", "i/$id" ); + $limit--; + } $oldContent = ContentHandler::makeContent( $wiki, $title ); $newContent = ContentHandler::makeContent( $params['content'], $title ); @@ -396,13 +422,18 @@ class SpecialManageGroups extends SpecialPage { $reader = \Cdb\Reader::open( $this->cdb ); $groups = $this->getGroupsFromCdb( $reader ); + $groupSyncCacheEnabled = $this->getConfig()->get( 'TranslateGroupSynchronizationCache' ); $postponed = []; foreach ( $groups as $groupId => $group ) { try { - $sourceChanges = MessageSourceChange::loadModifications( - TranslateUtils::deserialize( $reader->get( $groupId ) ) - ); + $changes = TranslateUtils::deserialize( $reader->get( $groupId ) ); + if ( $groupSyncCacheEnabled && $this->synchronizationCache->groupHasErrors( $groupId ) ) { + $postponed[$groupId] = $changes; + continue; + } + + $sourceChanges = MessageSourceChange::loadModifications( $changes ); $groupModificationJobs = []; $groupRenameJobData = []; $languages = $sourceChanges->getLanguages(); @@ -433,6 +464,10 @@ class SpecialManageGroups extends SpecialPage { } } + if ( $groupSyncCacheEnabled && !isset( $postponed[ $groupId ] ) ) { + $this->synchronizationCache->markGroupAsReviewed( $groupId ); + } + $modificationJobs[$groupId] = $groupModificationJobs; $renameJobData[$groupId] = $groupRenameJobData; } catch ( Exception $e ) { @@ -504,13 +539,10 @@ class SpecialManageGroups extends SpecialPage { return; } - $skin->getOutput()->addModuleStyles( 'ext.translate.tabgroup' ); - $tabs['namespaces'] = []; foreach ( $pagesInGroup as $spName => $section ) { $spClass = $specialPageFactory->getPage( $spName ); - // DisabledSpecialPage was added in MW 1.33 if ( $spClass === null || $spClass instanceof DisabledSpecialPage ) { continue; // Page explicitly disabled } @@ -558,7 +590,9 @@ class SpecialManageGroups extends SpecialPage { ): void { $changes = $sourceChanges->getRenames( $language ); foreach ( $changes as $key => $params ) { - if ( !isset( $changes[$key] ) ) { + // Since we're removing items from the array within the loop add + // a check here to ensure that the current key is still set. + if ( !isset( $changes[ $key ] ) ) { continue; } @@ -753,7 +787,9 @@ class SpecialManageGroups extends SpecialPage { $groupNamespace = $group->getNamespace(); foreach ( $renames as $key => $params ) { - if ( !isset( $renames[ $key] ) ) { + // Since we're removing items from the array within the loop add + // a check here to ensure that the current key is still set. + if ( !isset( $renames[$key] ) ) { continue; } @@ -943,13 +979,12 @@ class SpecialManageGroups extends SpecialPage { return [ false, $isCurrentKeyPresent ]; } - if ( $isSourceLang === false && $sourceChanges->isEqual( $language, $matchedKey ) ) { - // For non source language, if strings are equal, they are not shown on the UI - // and hence not submitted. - return [ false, $isCurrentKeyPresent ]; - } - - return [ true, $isCurrentKeyPresent ]; + // For non source language, if strings are equal, they are not shown on the UI + // and hence not submitted. + return [ + $isSourceLang || !$sourceChanges->isEqual( $language, $matchedKey ), + $isCurrentKeyPresent + ]; } private function getProcessingErrorMessage( array $errorGroups, int $totalGroupCount ): string { @@ -1037,11 +1072,26 @@ class SpecialManageGroups extends SpecialPage { if ( $this->getConfig()->get( 'TranslateGroupSynchronizationCache' ) ) { $this->synchronizationCache->addMessages( $groupId, ...$messages ); $this->synchronizationCache->markGroupForSync( $groupId ); + + LoggerFactory::getInstance( 'Translate.GroupSynchronization' )->info( + '[' . __CLASS__ . '] Synchronization started for {groupId} by {user}', + [ + 'groupId' => $groupId, + 'user' => $this->getUser()->getName() + ] + ); } - $jobQueueInstance->push( $groupJobs ); - } + // There is posibility for a race condition here: the translate_cache table / group sync + // cache is not yet populated with the messages to be processed, but the jobs start + // running and try to remove the message from the cache. This results in a "Key not found" + // error. Avoid this condition by using a DeferredUpdate. + DeferredUpdates::addCallableUpdate( + static function () use ( $jobQueueInstance, $groupJobs ) { + $jobQueueInstance->push( $groupJobs ); + } + ); - $jobQueueInstance->push( MessageIndexRebuildJob::newJob() ); + } } } diff --git a/MLEB/Translate/specials/SpecialMessageGroupStats.php b/MLEB/Translate/specials/SpecialMessageGroupStats.php index 338f70c0..8bf25814 100644 --- a/MLEB/Translate/specials/SpecialMessageGroupStats.php +++ b/MLEB/Translate/specials/SpecialMessageGroupStats.php @@ -146,7 +146,7 @@ class SpecialMessageGroupStats extends SpecialLanguageStats { sort( $languages ); $this->filterPriorityLangs( $languages, $this->target, $stats ); foreach ( $languages as $code ) { - if ( $table->isExcluded( $this->target, $code ) !== null ) { + if ( $table->isExcluded( $this->target, $code ) ) { continue; } $out .= $this->makeRow( $code, $stats ); diff --git a/MLEB/Translate/specials/SpecialSearchTranslations.php b/MLEB/Translate/specials/SpecialSearchTranslations.php index dd6fcd1d..bc129579 100644 --- a/MLEB/Translate/specials/SpecialSearchTranslations.php +++ b/MLEB/Translate/specials/SpecialSearchTranslations.php @@ -7,6 +7,9 @@ * @license GPL-2.0-or-later */ +use MediaWiki\Extension\Translate\TranslatorInterface\Aid\CurrentTranslationAid; +use MediaWiki\Extension\Translate\TranslatorInterface\Aid\TranslationAidDataProvider; + /** * ... * diff --git a/MLEB/Translate/specials/SpecialTranslate.php b/MLEB/Translate/specials/SpecialTranslate.php index 7294e6f0..2c20c41d 100644 --- a/MLEB/Translate/specials/SpecialTranslate.php +++ b/MLEB/Translate/specials/SpecialTranslate.php @@ -272,7 +272,7 @@ class SpecialTranslate extends SpecialPage { 'class' => $groupClass, 'data-msggroupid' => $this->group->getId(), ], - $this->group->getLabel() + $this->group->getLabel( $this->getContext() ) ) . Html::closeElement( 'div' ); @@ -360,8 +360,6 @@ class SpecialTranslate extends SpecialPage { return true; } - $skin->getOutput()->addModuleStyles( 'ext.translate.tabgroup' ); - // Extract subpage syntax, otherwise the values are not passed forward $params = []; if ( trim( $sub ) !== '' ) { diff --git a/MLEB/Translate/specials/SpecialTranslationStats.php b/MLEB/Translate/specials/SpecialTranslationStats.php deleted file mode 100644 index abc7e085..00000000 --- a/MLEB/Translate/specials/SpecialTranslationStats.php +++ /dev/null @@ -1,298 +0,0 @@ -<?php -/** - * Contains logic for special page Special:TranslationStats. - * - * @file - * @author Niklas Laxström - * @author Siebrand Mazeland - * @license GPL-2.0-or-later - */ - -use MediaWiki\Extension\Translate\Services; -use MediaWiki\Extension\Translate\Statistics\TranslationStatsGraphOptions; - -/** - * @defgroup Stats Statistics - * Collection of code to produce various kinds of statistics. - */ - -/** - * Includable special page for generating graphs on translations. - * - * @ingroup SpecialPage TranslateSpecialPage Stats - */ -class SpecialTranslationStats extends SpecialPage { - - /** @var \MediaWiki\Extension\Translate\Statistics\TranslationStatsDataProvider */ - private $dataProvider; - - private const GRAPH_CONTAINER_ID = 'translationStatsGraphContainer'; - - private const GRAPH_CONTAINER_CLASS = 'mw-translate-translationstats-container'; - - public function __construct() { - parent::__construct( 'TranslationStats' ); - $this->dataProvider = Services::getInstance()->getTranslationStatsDataProvider(); - } - - public function isIncludable() { - return true; - } - - protected function getGroupName() { - return 'translation'; - } - - public function execute( $par ) { - $graphOpts = new TranslationStatsGraphOptions(); - $graphOpts->bindArray( $this->getRequest()->getValues() ); - - $pars = explode( ';', $par ); - foreach ( $pars as $item ) { - if ( strpos( $item, '=' ) === false ) { - continue; - } - - list( $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. - * @param FormOptions $opts - * @suppress SecurityCheck-DoubleEscaped Intentionally outputting what user should type - */ - protected function form( FormOptions $opts ) { - global $wgScript; - $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' => $wgScript, '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, [ '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 ( $spiParams !== '' ) { - $spiParams .= ';'; - } - - if ( is_array( $v ) ) { - $v = implode( ',', $v ); - if ( !strlen( $v ) ) { - continue; - } - } - $spiParams .= wfEscapeWikiText( "$key=$v" ); - } - if ( $spiParams !== '' ) { - $spiParams = '/' . $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}}}" - ) - ); - } - - /** - * Constructs a table row with label and input in two columns. - * @param string $name Option name. - * @param FormOptions $opts - * @param int $width - * @return string Html. - */ - protected function eInput( $name, FormOptions $opts, $width = 4 ) { - $value = $opts[$name]; - return '<tr><td>' . $this->eLabel( $name ) . '</td><td>' . - Xml::input( $name, $width, $value, [ 'id' => $name ] ) . - '</td></tr>' . "\n"; - } - - /** - * Constructs a label for option. - * @param string $name Option name. - * @return string Html. - */ - protected function eLabel( $name ) { - // 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 ); - } - - /** - * Constructs a table row with label and radio input in two columns. - * @param string $name Option name. - * @param FormOptions $opts - * @param string[] $alts List of alternatives. - * @return string Html. - */ - protected function eRadio( $name, FormOptions $opts, array $alts ) { - // 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; - } - - /** - * Constructs a table row with label and language selector in two columns. - * @param string $name Option name. - * @param FormOptions $opts - * @return string Html. - */ - protected function eLanguage( $name, FormOptions $opts ) { - $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"; - } - - /** - * Constructs a JavaScript enhanced language selector. - * @return JsSelectToInput - */ - protected function languageSelector() { - $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 ); - } - $jsSelect = new JsSelectToInput( $selector ); - return $jsSelect; - } - - /** - * Constructs a table row with label and group selector in two columns. - * @param string $name Option name. - * @param FormOptions $opts - * @return string Html. - */ - protected function eGroup( $name, FormOptions $opts ) { - $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"; - } - - /** - * Constructs a JavaScript enhanced group selector. - * @return JsSelectToInput - */ - protected function groupSelector() { - $groups = MessageGroups::singleton()->getGroups(); - /** @var MessageGroup $group */ - foreach ( $groups as $key => $group ) { - if ( !$group->exists() ) { - unset( $groups[$key] ); - continue; - } - } - ksort( $groups ); - $selector = new XmlSelect( 'mw-group-selector', 'mw-group-selector' ); - /** @var MessageGroup $name */ - foreach ( $groups as $code => $name ) { - $selector->addOption( $name->getLabel(), $code ); - } - $jsSelect = new JsSelectToInput( $selector ); - return $jsSelect; - } - - protected function embed( FormOptions $opts ) { - $this->getOutput()->addModules( 'ext.translate.translationstats.embedded' ); - return Html::rawElement( - 'div', - [ - 'class' => self::GRAPH_CONTAINER_CLASS - ], - Html::hidden( - 'translationStatsGraphOptions', - json_encode( $opts->getAllValues() ) - ) - ); - } -} diff --git a/MLEB/Translate/specials/SpecialTranslations.php b/MLEB/Translate/specials/SpecialTranslations.php index ebdf1787..87632a2f 100644 --- a/MLEB/Translate/specials/SpecialTranslations.php +++ b/MLEB/Translate/specials/SpecialTranslations.php @@ -167,10 +167,7 @@ class SpecialTranslations extends SpecialAllPages { 'page_title ' . $dbr->buildLike( "$message/", $dbr->anyString() ), ], __METHOD__, - [ - 'ORDER BY' => 'page_title', - 'USE INDEX' => 'name_title', - ] + [ 'ORDER BY' => 'page_title', ] ); if ( !$res->numRows() ) { @@ -246,7 +243,7 @@ class SpecialTranslations extends SpecialAllPages { $languageAttributes = []; if ( Language::isKnownLanguageTag( $code ) ) { - $language = Language::factory( $code ); + $language = $tHandle->getEffectiveLanguage(); $languageAttributes = [ 'lang' => $language->getHtmlCode(), 'dir' => $language->getDir(), |