diff options
Diffstat (limited to 'MLEB/Translate/utils')
27 files changed, 397 insertions, 728 deletions
diff --git a/MLEB/Translate/utils/ArrayFlattener.php b/MLEB/Translate/utils/ArrayFlattener.php index db57b4f5..5e8d91bc 100644 --- a/MLEB/Translate/utils/ArrayFlattener.php +++ b/MLEB/Translate/utils/ArrayFlattener.php @@ -14,7 +14,6 @@ class ArrayFlattener { protected $sep; protected $parseCLDRPlurals; - // For CLDR pluralization rules protected static $pluralWords = [ 'zero' => 1, diff --git a/MLEB/Translate/utils/ExternalMessageSourceStateComparator.php b/MLEB/Translate/utils/ExternalMessageSourceStateComparator.php index a6b53f4f..9b4b298d 100644 --- a/MLEB/Translate/utils/ExternalMessageSourceStateComparator.php +++ b/MLEB/Translate/utils/ExternalMessageSourceStateComparator.php @@ -8,21 +8,17 @@ * @since 2013.12 */ -use MediaWiki\Extensions\Translate\MessageSync\MessageSourceChange; -use MediaWiki\Extensions\Translate\Utilities\StringComparators\StringComparator; +use MediaWiki\Extension\Translate\MessageSync\MessageSourceChange; +use MediaWiki\Extension\Translate\Utilities\StringComparators\StringComparator; class ExternalMessageSourceStateComparator { /** Process all languages supported by the message group */ public const ALL_LANGUAGES = 'all languages'; - /** - * @var StringComparator - */ + /** @var StringComparator */ protected $stringComparator; - /** - * @param StringComparator $stringComparator - */ + /** @param StringComparator $stringComparator */ public function __construct( StringComparator $stringComparator ) { $this->stringComparator = $stringComparator; } @@ -129,9 +125,8 @@ class ExternalMessageSourceStateComparator { ) { /* This throws a warning if message definitions are not yet * cached and will read the file for definitions. */ - Wikimedia\suppressWarnings(); - $wiki = $group->initCollection( $language ); - Wikimedia\restoreWarnings(); + // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged + $wiki = @$group->initCollection( $language ); $wiki->filter( 'hastranslation', false ); $wiki->loadTranslations(); $wikiKeys = $wiki->getMessageKeys(); @@ -233,10 +228,6 @@ class ExternalMessageSourceStateComparator { $added = array_diff( $fileKeys, $wikiKeys ); foreach ( $added as $key ) { $sourceContent = $file['MESSAGES'][$key]; - if ( trim( $sourceContent ) === '' ) { - continue; - } - $changes->addAddition( $language, $key, $sourceContent ); } diff --git a/MLEB/Translate/utils/ExternalMessageSourceStateImporter.php b/MLEB/Translate/utils/ExternalMessageSourceStateImporter.php index 51674921..5fa3f471 100644 --- a/MLEB/Translate/utils/ExternalMessageSourceStateImporter.php +++ b/MLEB/Translate/utils/ExternalMessageSourceStateImporter.php @@ -8,7 +8,10 @@ * @since 2016.02 */ -use MediaWiki\Extensions\Translate\MessageSync\MessageSourceChange; +use MediaWiki\Extension\Translate\MessageSync\MessageSourceChange; +use MediaWiki\Extension\Translate\Services; +use MediaWiki\Extension\Translate\Synchronization\MessageUpdateParameter; +use MediaWiki\MediaWikiServices; class ExternalMessageSourceStateImporter { @@ -22,13 +25,9 @@ class ExternalMessageSourceStateImporter { $jobs = []; $jobs[] = MessageIndexRebuildJob::newJob(); - /** - * @var MessageSourceChange $changesForGroup - */ + /** @var MessageSourceChange $changesForGroup */ foreach ( $changeData as $groupId => $changesForGroup ) { - /** - * @var FileBasedMessageGroup - */ + /** @var FileBasedMessageGroup */ $group = MessageGroups::getGroup( $groupId ); if ( !$group ) { unset( $changeData[$groupId] ); @@ -38,10 +37,12 @@ class ExternalMessageSourceStateImporter { $processed[$groupId] = []; $languages = $changesForGroup->getLanguages(); + $groupJobs = []; + + $groupSafeLanguages = self::identifySafeLanguages( $group, $changesForGroup ); foreach ( $languages as $language ) { - if ( !self::isSafe( $changesForGroup, $language ) ) { - // changes other than additions were present + if ( !$groupSafeLanguages[ $language ] ) { $skipped[$groupId] = true; continue; } @@ -51,16 +52,21 @@ class ExternalMessageSourceStateImporter { continue; } - [ $groupJobs, $groupProcessed ] = $this->createMessageUpdateJobs( + [ $groupLanguageJobs, $groupProcessed ] = $this->createMessageUpdateJobs( $group, $additions, $language ); - $jobs = array_merge( $jobs, $groupJobs ); + $groupJobs = array_merge( $groupJobs, $groupLanguageJobs ); $processed[$groupId][$language] = $groupProcessed; $changesForGroup->removeChangesForLanguage( $language ); $group->getMessageGroupCache( $language )->create(); } + + if ( $groupJobs !== [] ) { + $this->updateGroupSyncInfo( $groupId, $groupJobs ); + $jobs = array_merge( $jobs, $groupJobs ); + } } // Remove groups where everything was imported @@ -84,16 +90,6 @@ class ExternalMessageSourceStateImporter { } /** - * Checks if changes for a language in a group are safe. - * @param MessageSourceChange $changesForGroup - * @param string $language - * @return bool - */ - public static function isSafe( MessageSourceChange $changesForGroup, $language ) { - return $changesForGroup->hasOnly( $language, MessageSourceChange::ADDITION ); - } - - /** * Creates MessagUpdateJobs additions for a language under a group * * @param MessageGroup $group @@ -123,4 +119,111 @@ class ExternalMessageSourceStateImporter { return [ $jobs, $processed ]; } + + /** + * @param string $groupId + * @param MessageUpdateJob[] $groupJobs + */ + private function updateGroupSyncInfo( string $groupId, array $groupJobs ): void { + $config = MediaWikiServices::getInstance()->getMainConfig(); + + if ( !$config->get( 'TranslateGroupSynchronizationCache' ) ) { + return; + } + + $messageParams = []; + $groupMessageKeys = []; + foreach ( $groupJobs as $job ) { + $messageParams[] = MessageUpdateParameter::createFromJob( $job ); + // Ensure there are no duplicates as the same key may be present in + // multiple languages + $groupMessageKeys[( new MessageHandle( $job->getTitle() ) )->getKey()] = true; + } + + $group = MessageGroups::getGroup( $groupId ); + if ( $group === null ) { + // How did we get here? This should never happen. + throw new RuntimeException( "Did not find group $groupId" ); + } + + MessageIndex::singleton()->storeInterim( $group, array_keys( $groupMessageKeys ) ); + + $groupSyncCache = Services::getInstance()->getGroupSynchronizationCache(); + $groupSyncCache->addMessages( $groupId, ...$messageParams ); + $groupSyncCache->markGroupForSync( $groupId ); + } + + /** + * Identifies languages in a message group that are safe to import + * @param MessageGroup $group + * @param MessageSourceChange $changesForGroup + * @return bool[] + */ + private static function identifySafeLanguages( + MessageGroup $group, + MessageSourceChange $changesForGroup + ): array { + $sourceLanguage = $group->getSourceLanguage(); + $safeLanguagesMap = []; + $modifiedLanguages = $changesForGroup->getLanguages(); + + // Set all languages to not safe to start with. + $safeLanguagesMap[ $sourceLanguage ] = false; + foreach ( $modifiedLanguages as $language ) { + $safeLanguagesMap[ $language ] = false; + } + + if ( !$changesForGroup->hasOnly( $sourceLanguage, MessageSourceChange::ADDITION ) ) { + return $safeLanguagesMap; + } + + $sourceLanguageKeyCache = []; + foreach ( $changesForGroup->getAdditions( $sourceLanguage ) as $change ) { + if ( $change['content'] === '' ) { + return $safeLanguagesMap; + } + + $sourceLanguageKeyCache[ $change['key'] ] = true; + } + + $safeLanguagesMap[ $sourceLanguage ] = true; + + $groupNamespace = $group->getNamespace(); + + // Remove source language from the modifiedLanguage list if present since it's already processed. + // The $sourceLanguageKeyCache will only have values if sourceLanguage has safe changes. + if ( $sourceLanguageKeyCache ) { + array_splice( $modifiedLanguages, array_search( $sourceLanguage, $modifiedLanguages ), 1 ); + } + + foreach ( $modifiedLanguages as $language ) { + if ( !$changesForGroup->hasOnly( $language, MessageSourceChange::ADDITION ) ) { + continue; + } + + foreach ( $changesForGroup->getAdditions( $language ) as $change ) { + if ( $change['content'] === '' ) { + continue 2; + } + + $msgKey = $change['key']; + + if ( !isset( $sourceLanguageKeyCache[ $msgKey ] ) ) { + // This is either a new external translation which is not added in the same sync + // as the source language key, or this translation does not have a correspoding + // definition. We will check the message index to determine which of the two. + $sourceHandle = new MessageHandle( Title::makeTitle( $groupNamespace, $msgKey ) ); + $sourceLanguageKeyCache[ $msgKey ] = $sourceHandle->isValid(); + } + + if ( !$sourceLanguageKeyCache[ $msgKey ] ) { + continue 2; + } + } + + $safeLanguagesMap[ $language ] = true; + } + + return $safeLanguagesMap; + } } diff --git a/MLEB/Translate/utils/FCFontFinder.php b/MLEB/Translate/utils/FCFontFinder.php deleted file mode 100644 index 37fa4ac7..00000000 --- a/MLEB/Translate/utils/FCFontFinder.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php -/** - * Contains class with wrapper around font-config. - * - * @author Niklas Laxström - * @author Harry Burt - * @license Unlicense - * @file - */ - -/** - * Wrapper around font-config to get useful ttf font given a language code. - * Uses wfShellExec, wfEscapeShellArg and wfDebugLog, wfGetCache and - * wfMemckey from %MediaWiki. - * - * @ingroup Stats - */ -class FCFontFinder { - /** - * Searches for suitable font in the system. - * @param string $code Language code. - * @return bool|string Full path to the font file, false on failure - */ - public static function findFile( $code ) { - $data = self::callFontConfig( $code ); - if ( is_array( $data ) ) { - return $data['file']; - } - - return false; - } - - /** - * Searches for suitable font family in the system. - * @param string $code Language code. - * @return bool|string Name of font family, false on failure - */ - public static function findFamily( $code ) { - $data = self::callFontConfig( $code ); - if ( is_array( $data ) ) { - return $data['family']; - } - - return false; - } - - protected static function callFontConfig( $code ) { - if ( ini_get( 'open_basedir' ) ) { - wfDebugLog( 'fcfont', 'Disabled because of open_basedir is active' ); - - // Most likely we can't access any fonts we might find - return false; - } - - $cache = self::getCache(); - $cachekey = wfMemcKey( 'fcfont', $code ); - $timeout = 60 * 60 * 12; - - $cached = $cache->get( $cachekey ); - if ( is_array( $cached ) ) { - return $cached; - } elseif ( $cached === 'NEGATIVE' ) { - return false; - } - - $code = wfEscapeShellArg( ":lang=$code" ); - $ok = 0; - $cmd = "fc-match $code"; - $suggestion = wfShellExec( $cmd, $ok ); - - wfDebugLog( 'fcfont', "$cmd returned $ok" ); - - if ( $ok !== 0 ) { - wfDebugLog( 'fcfont', "fc-match error output: $suggestion" ); - $cache->set( $cachekey, 'NEGATIVE', $timeout ); - - return false; - } - - $pattern = '/^(.*?): "(.*)" "(.*)"$/'; - $matches = []; - - if ( !preg_match( $pattern, $suggestion, $matches ) ) { - wfDebugLog( 'fcfont', "fc-match: return format not understood: $suggestion" ); - $cache->set( $cachekey, 'NEGATIVE', $timeout ); - - return false; - } - - list( , $file, $family, $type ) = $matches; - wfDebugLog( 'fcfont', "fc-match: got $file: $family $type" ); - - $file = wfEscapeShellArg( $file ); - $family = wfEscapeShellArg( $family ); - $type = wfEscapeShellArg( $type ); - $cmd = "fc-list $family $type $code file | grep $file"; - - $candidates = trim( wfShellExec( $cmd, $ok ) ); - - wfDebugLog( 'fcfont', "$cmd returned $ok" ); - - if ( $ok !== 0 ) { - wfDebugLog( 'fcfont', "fc-list error output: $candidates" ); - $cache->set( $cachekey, 'NEGATIVE', $timeout ); - - return false; - } - - # trim spaces - $files = array_map( 'trim', explode( "\n", $candidates ) ); - $count = count( $files ); - if ( !$count ) { - wfDebugLog( 'fcfont', "fc-list got zero canditates: $candidates" ); - } - - # remove the trailing ":" - $chosen = substr( $files[0], 0, -1 ); - - wfDebugLog( 'fcfont', "fc-list got $count candidates; using $chosen" ); - - $data = [ - 'family' => $family, - 'type' => $type, - 'file' => $chosen, - ]; - - $cache->set( $cachekey, $data, $timeout ); - - return $data; - } - - /** - * @return BagOStuff - */ - protected static function getCache() { - return wfGetCache( CACHE_ANYTHING ); - } -} diff --git a/MLEB/Translate/utils/HTMLJsSelectToInputField.php b/MLEB/Translate/utils/HTMLJsSelectToInputField.php index 57f4443c..d3b22472 100644 --- a/MLEB/Translate/utils/HTMLJsSelectToInputField.php +++ b/MLEB/Translate/utils/HTMLJsSelectToInputField.php @@ -19,9 +19,7 @@ class HTMLJsSelectToInputField extends HTMLTextField { $input = parent::getInputHTML( $value ); if ( isset( $this->mParams['select'] ) ) { - /** - * @var JsSelectToInput $select - */ + /** @var JsSelectToInput $select */ $select = $this->mParams['select']; $input = $select->getHtmlAndPrepareJS() . '<br />' . $input; } diff --git a/MLEB/Translate/utils/JsSelectToInput.php b/MLEB/Translate/utils/JsSelectToInput.php index 83d5cc65..188b5e0f 100644 --- a/MLEB/Translate/utils/JsSelectToInput.php +++ b/MLEB/Translate/utils/JsSelectToInput.php @@ -15,27 +15,18 @@ class JsSelectToInput { protected $targetId; /// Id of the \<option> field protected $sourceId; - - /** - * @var XmlSelect - */ + /** @var XmlSelect */ protected $select; - /// Id on the button protected $buttonId; - - /** - * @var string Text for the append button - */ + /** @var string Text for the append button */ protected $msg = 'translate-jssti-add'; public function __construct( XmlSelect $select = null ) { $this->select = $select; } - /** - * @return string - */ + /** @return string */ public function getSourceId() { return $this->sourceId; } @@ -48,9 +39,7 @@ class JsSelectToInput { $this->targetId = $id; } - /** - * @return string - */ + /** @return string */ public function getTargetId() { return $this->targetId; } @@ -63,9 +52,7 @@ class JsSelectToInput { $this->msg = $message; } - /** - * @return string Message key. - */ + /** @return string Message key. */ public function getMessage() { return $this->msg; } diff --git a/MLEB/Translate/utils/MemProfile.php b/MLEB/Translate/utils/MemProfile.php deleted file mode 100644 index 7ebc3423..00000000 --- a/MLEB/Translate/utils/MemProfile.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -if ( !defined( 'MEDIAWIKI' ) ) { - die(); -} -/** - * Very crude tools to track memory usage - * - * @file - * @author Niklas Laxström - * @copyright Copyright © 2008, Niklas Laxström - * @license GPL-2.0-or-later - */ - -/// Memory usage at checkpoints -$wgMemUse = []; -/// Tracks the deepness of the stack -$wgMemStack = 0; - -/** - * Call to start memory counting for a block. - * @param string $a Block name. - */ -function wfMemIn( $a ) { - global $wgLang, $wgMemUse, $wgMemStack; - - $mem = memory_get_usage(); - $memR = memory_get_usage(); - - $wgMemUse[$a][] = [ $mem, $memR ]; - - $memF = $wgLang->formatNum( $mem ); - $memRF = $wgLang->formatNum( $memR ); - - $pad = str_repeat( '.', $wgMemStack ); - wfDebug( "$pad$a-IN: \t$memF\t\t$memRF\n" ); - $wgMemStack++; -} - -/** - * Call to start stop counting for a block. Difference from start is shown. - * @param string $a Block name. - */ -function wfMemOut( $a ) { - global $wgLang, $wgMemUse, $wgMemStack; - - $mem = memory_get_usage(); - $memR = memory_get_usage(); - - list( $memO, $memOR ) = array_pop( $wgMemUse[$a] ); - - $memF = $wgLang->formatNum( $mem ); - $memRF = $wgLang->formatNum( $memR ); - - $memD = $mem - $memO; - $memRD = $memR - $memOR; - - $memDF = $wgLang->formatNum( $memD ); - $memRDF = $wgLang->formatNum( $memRD ); - - $pad = str_repeat( '.', $wgMemStack - 1 ); - wfDebug( "$pad$a-OUT:\t$memF ($memDF)\t$memRF ($memRDF)\n" ); - $wgMemStack--; -} diff --git a/MLEB/Translate/utils/MessageChangeStorage.php b/MLEB/Translate/utils/MessageChangeStorage.php index 6fbd8a0a..b53f966c 100644 --- a/MLEB/Translate/utils/MessageChangeStorage.php +++ b/MLEB/Translate/utils/MessageChangeStorage.php @@ -8,7 +8,7 @@ * @file */ -use MediaWiki\Extensions\Translate\MessageSync\MessageSourceChange; +use MediaWiki\Extension\Translate\MessageSync\MessageSourceChange; class MessageChangeStorage { public const DEFAULT_NAME = 'default'; @@ -25,9 +25,7 @@ class MessageChangeStorage { $keys = array_keys( $changes ); $cache->set( '#keys', TranslateUtils::serialize( $keys ) ); - /** - * @var MessageSourceChange $change - */ + /** @var MessageSourceChange $change */ foreach ( $changes as $key => $change ) { $value = TranslateUtils::serialize( $change->getAllModifications() ); $cache->set( $key, $value ); diff --git a/MLEB/Translate/utils/MessageGroupCache.php b/MLEB/Translate/utils/MessageGroupCache.php index fdb1fc01..fa174a35 100644 --- a/MLEB/Translate/utils/MessageGroupCache.php +++ b/MLEB/Translate/utils/MessageGroupCache.php @@ -19,24 +19,13 @@ class MessageGroupCache { public const NO_CACHE = 2; public const CHANGED = 3; - /** - * @var FileBasedMessageGroup - */ + /** @var FileBasedMessageGroup */ protected $group; - - /** - * @var \Cdb\Reader - */ + /** @var \Cdb\Reader */ protected $cache; - - /** - * @var string - */ + /** @var string */ protected $code; - - /** - * @var string - */ + /** @var string */ private $cacheFilePath; /** @@ -253,6 +242,11 @@ class MessageGroupCache { return false; } + public function invalidate(): void { + $this->close(); + unlink( $this->getCacheFilePath() ); + } + private function serialize( array $data ): string { // Using simple prefix for easy future extension return 'J' . json_encode( $data ); diff --git a/MLEB/Translate/utils/MessageGroupStatesUpdaterJob.php b/MLEB/Translate/utils/MessageGroupStatesUpdaterJob.php index dda4a8f9..c261106e 100644 --- a/MLEB/Translate/utils/MessageGroupStatesUpdaterJob.php +++ b/MLEB/Translate/utils/MessageGroupStatesUpdaterJob.php @@ -8,14 +8,16 @@ * @license GPL-2.0-or-later */ -use MediaWiki\Extensions\Translate\SystemUsers\FuzzyBot; +use MediaWiki\Extension\Translate\Jobs\GenericTranslateJob; +use MediaWiki\Extension\Translate\SystemUsers\FuzzyBot; +use MediaWiki\MediaWikiServices; /** * Logic for handling automatic message group state changes * * @ingroup JobQueue */ -class MessageGroupStatesUpdaterJob extends Job { +class MessageGroupStatesUpdaterJob extends GenericTranslateJob { /** * @param Title $title * @param array $params @@ -49,6 +51,11 @@ class MessageGroupStatesUpdaterJob extends Job { } public function run() { + $lb = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); + if ( !$lb->waitForReplication() ) { + $this->logWarning( 'Continuing despite replication lag' ); + } + $title = $this->title; $handle = new MessageHandle( $title ); $code = $handle->getCode(); diff --git a/MLEB/Translate/utils/MessageGroupStats.php b/MLEB/Translate/utils/MessageGroupStats.php index 91267f22..dc7b1133 100644 --- a/MLEB/Translate/utils/MessageGroupStats.php +++ b/MLEB/Translate/utils/MessageGroupStats.php @@ -8,6 +8,7 @@ * @license GPL-2.0-or-later */ +use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; use Wikimedia\Rdbms\IDatabase; @@ -34,14 +35,9 @@ class MessageGroupStats { /// Do not defer updates. Meant for jobs like MessageGroupStatsRebuildJob. public const FLAG_IMMEDIATE_WRITES = 4; - /** - * @var array[] - */ + /** @var array[] */ protected static $updates = []; - - /** - * @var string[] - */ + /** @var string[] */ private static $languages; /** @@ -547,9 +543,7 @@ class MessageGroupStats { if ( $code === $wgTranslateDocumentationLanguageCode ) { $ffs = $group->getFFS(); if ( $ffs instanceof GettextFFS ) { - /** - * @var FileBasedMessageGroup $group - */ + /** @var FileBasedMessageGroup $group */ '@phan-var FileBasedMessageGroup $group'; $cache = $group->getMessageGroupCache( $group->getSourceLanguage() ); if ( $cache->exists() ) { @@ -566,7 +560,7 @@ class MessageGroupStats { } $collection->filter( 'ignored' ); - $collection->filter( 'optional' ); + $collection->filterUntranslatedOptional(); // Store the count of real messages for later calculation. $total = count( $collection ); @@ -626,6 +620,18 @@ class MessageGroupStats { return; } + // This path should only be hit during web requests + if ( count( $updates ) > 100 ) { + $groups = array_unique( array_column( $updates, 'tgs_group' ) ); + LoggerFactory::getInstance( 'Translate' )->warning( + "Huge translation update of {count} rows for group(s) {groups}", + [ + 'count' => count( $updates ), + 'groups' => implode( ', ', $groups ), + ] + ); + } + $primaryKey = [ 'tgs_group', 'tgs_lang' ]; $dbw->replace( $table, [ $primaryKey ], $updates, $method ); $updates = []; diff --git a/MLEB/Translate/utils/MessageGroupStatsRebuildJob.php b/MLEB/Translate/utils/MessageGroupStatsRebuildJob.php index e813d614..53c3a496 100644 --- a/MLEB/Translate/utils/MessageGroupStatsRebuildJob.php +++ b/MLEB/Translate/utils/MessageGroupStatsRebuildJob.php @@ -7,12 +7,15 @@ * @license GPL-2.0-or-later */ +use MediaWiki\Extension\Translate\Jobs\GenericTranslateJob; +use MediaWiki\MediaWikiServices; + /** * Job for rebuilding message group stats. * * @ingroup JobQueue */ -class MessageGroupStatsRebuildJob extends Job { +class MessageGroupStatsRebuildJob extends GenericTranslateJob { /** * @param array $params * @return self @@ -45,6 +48,11 @@ class MessageGroupStatsRebuildJob extends Job { } public function run() { + $lb = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); + if ( !$lb->waitForReplication() ) { + $this->logWarning( 'Continuing despite replication lag' ); + } + $params = $this->params; $flags = 0; diff --git a/MLEB/Translate/utils/MessageGroupWANCache.php b/MLEB/Translate/utils/MessageGroupWANCache.php index 4df5b4c7..78d8e817 100644 --- a/MLEB/Translate/utils/MessageGroupWANCache.php +++ b/MLEB/Translate/utils/MessageGroupWANCache.php @@ -14,50 +14,41 @@ */ class MessageGroupWANCache { - /** - * @var WANObjectCache - */ + /** @var WANObjectCache */ protected $cache; - /** * Cache key * * @var string */ protected $cacheKey; - /** * Cache version * * @var int */ protected $cacheVersion; - /** * To be called when the cache is empty or expired to get the data * to repopulate the cache * @var \Closure */ protected $regenerator; - /** * @see @https://doc.wikimedia.org/mediawiki-core/master/php/classWANObjectCache.html * @var int */ protected $lockTSE; - /** * @see @https://doc.wikimedia.org/mediawiki-core/master/php/classWANObjectCache.html * @var array */ protected $checkKeys; - /** * @see @https://doc.wikimedia.org/mediawiki-core/master/php/classWANObjectCache.html * @var \Closure */ protected $touchedCallback; - /** * @see @https://doc.wikimedia.org/mediawiki-core/master/php/classWANObjectCache.html * @var int diff --git a/MLEB/Translate/utils/MessageHandle.php b/MLEB/Translate/utils/MessageHandle.php index bb9c43c5..3d95608f 100644 --- a/MLEB/Translate/utils/MessageHandle.php +++ b/MLEB/Translate/utils/MessageHandle.php @@ -16,24 +16,13 @@ use MediaWiki\MediaWikiServices; * @since 2011-03-13 */ class MessageHandle { - /** - * @var LinkTarget - */ + /** @var LinkTarget */ protected $title; - - /** - * @var string|null - */ + /** @var string|null */ protected $key; - - /** - * @var string|null Language code - */ + /** @var string|null Language code */ protected $code; - - /** - * @var string[]|null - */ + /** @var string[]|null */ protected $groupIds; public function __construct( LinkTarget $title ) { @@ -76,7 +65,7 @@ class MessageHandle { /** * Returns the identified or guessed message key. - * @return String + * @return string */ public function getKey() { $this->figureMessage(); @@ -87,7 +76,7 @@ class MessageHandle { /** * Returns the language code. * For language codeless source messages will return empty string. - * @return String + * @return string */ public function getCode() { $this->figureMessage(); @@ -272,7 +261,8 @@ class MessageHandle { public function getInternalKey() { $key = $this->getKey(); - if ( !MWNamespace::isCapitalized( $this->title->getNamespace() ) ) { + $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo(); + if ( !$nsInfo->isCapitalized( $this->title->getNamespace() ) ) { return $key; } diff --git a/MLEB/Translate/utils/MessageIndex.php b/MLEB/Translate/utils/MessageIndex.php index 128efe85..1cbdaf39 100644 --- a/MLEB/Translate/utils/MessageIndex.php +++ b/MLEB/Translate/utils/MessageIndex.php @@ -21,22 +21,14 @@ use MediaWiki\Logger\LoggerFactory; abstract class MessageIndex { private const CACHEKEY = 'Translate-MessageIndex-interim'; - /** - * @var self - */ + /** @var self */ protected static $instance; - - /** - * @var MapCacheLRU|null - */ + /** @var MapCacheLRU|null */ private static $keysCache; - /** @var BagOStuff */ protected $interimCache; - /** - * @return self - */ + /** @return self */ public static function singleton() { if ( self::$instance === null ) { global $wgTranslateMessageIndex; @@ -91,9 +83,7 @@ abstract class MessageIndex { return $value; } - /** - * @return MapCacheLRU - */ + /** @return MapCacheLRU */ private static function getCache() { if ( self::$keysCache === null ) { self::$keysCache = new MapCacheLRU( 30 ); @@ -202,9 +192,7 @@ abstract class MessageIndex { $old = $this->retrieve( 'rebuild' ); $postponed = []; - /** - * @var MessageGroup $g - */ + /** @var MessageGroup $g */ foreach ( $groups as $g ) { if ( !$g->exists() ) { $id = $g->getId(); @@ -451,11 +439,8 @@ abstract class MessageIndex { * which provides random access - this backend doesn't support that. */ class SerializedMessageIndex extends MessageIndex { - /** - * @var array|null - */ + /** @var array|null */ protected $index; - protected $filename = 'translate_messageindex.ser'; /** @@ -496,9 +481,7 @@ class SerializedMessageIndex extends MessageIndex { * @since 2012-04-12 */ class DatabaseMessageIndex extends MessageIndex { - /** - * @var array|null - */ + /** @var array|null */ protected $index; protected function lock() { @@ -618,10 +601,7 @@ class DatabaseMessageIndex extends MessageIndex { class CachedMessageIndex extends MessageIndex { protected $key = 'translate-messageindex'; protected $cache; - - /** - * @var array|null - */ + /** @var array|null */ protected $index; protected function __construct() { @@ -637,7 +617,7 @@ class CachedMessageIndex extends MessageIndex { return $this->index; } - $key = wfMemcKey( $this->key ); + $key = $this->cache->makeKey( $this->key ); $data = $this->cache->get( $key ); if ( is_array( $data ) ) { $this->index = $data; @@ -649,7 +629,7 @@ class CachedMessageIndex extends MessageIndex { } protected function store( array $array, array $diff ) { - $key = wfMemcKey( $this->key ); + $key = $this->cache->makeKey( $this->key ); $this->cache->set( $key, $array ); $this->index = $array; @@ -670,19 +650,11 @@ class CachedMessageIndex extends MessageIndex { * @since 2012-04-10 */ class CDBMessageIndex extends MessageIndex { - /** - * @var array|null - */ + /** @var array|null */ protected $index; - - /** - * @var \Cdb\Reader|null - */ + /** @var \Cdb\Reader|null */ protected $reader; - - /** - * @var string - */ + /** @var string */ protected $filename = 'translate_messageindex.cdb'; /** @@ -776,9 +748,7 @@ class CDBMessageIndex extends MessageIndex { * @since 2015.04 */ class HashMessageIndex extends MessageIndex { - /** - * @var array - */ + /** @var array */ protected $index = []; /** diff --git a/MLEB/Translate/utils/MessageIndexException.php b/MLEB/Translate/utils/MessageIndexException.php index b0e5c7a6..77916d1c 100644 --- a/MLEB/Translate/utils/MessageIndexException.php +++ b/MLEB/Translate/utils/MessageIndexException.php @@ -5,8 +5,6 @@ * @license GPL-2.0-or-later */ -/** - * @since 2020.05 - */ +/** @since 2020.05 */ class MessageIndexException extends RuntimeException { } diff --git a/MLEB/Translate/utils/MessageIndexRebuildJob.php b/MLEB/Translate/utils/MessageIndexRebuildJob.php index abcb7b91..a155ff9e 100644 --- a/MLEB/Translate/utils/MessageIndexRebuildJob.php +++ b/MLEB/Translate/utils/MessageIndexRebuildJob.php @@ -8,7 +8,7 @@ * @license GPL-2.0-or-later */ -use MediaWiki\Extensions\Translate\Jobs\GenericTranslateJob; +use MediaWiki\Extension\Translate\Jobs\GenericTranslateJob; /** * Job for rebuilding message index. @@ -16,9 +16,7 @@ use MediaWiki\Extensions\Translate\Jobs\GenericTranslateJob; * @ingroup JobQueue */ class MessageIndexRebuildJob extends GenericTranslateJob { - /** - * @return self - */ + /** @return self */ public static function newJob() { $timestamp = microtime( true ); $job = new self( Title::newMainPage(), [ 'timestamp' => $timestamp ] ); diff --git a/MLEB/Translate/utils/MessageUpdateJob.php b/MLEB/Translate/utils/MessageUpdateJob.php index 8d4ff35a..7ad7b998 100644 --- a/MLEB/Translate/utils/MessageUpdateJob.php +++ b/MLEB/Translate/utils/MessageUpdateJob.php @@ -8,9 +8,11 @@ * @license GPL-2.0-or-later */ -use MediaWiki\Extensions\Translate\Jobs\GenericTranslateJob; -use MediaWiki\Extensions\Translate\SystemUsers\FuzzyBot; -use MediaWiki\Extensions\Translate\Utilities\TranslateReplaceTitle; +use MediaWiki\Extension\Translate\Jobs\GenericTranslateJob; +use MediaWiki\Extension\Translate\Services; +use MediaWiki\Extension\Translate\SystemUsers\FuzzyBot; +use MediaWiki\Extension\Translate\Utilities\TranslateReplaceTitle; +use MediaWiki\MediaWikiServices; /** * Job for updating translation pages when translation or message definition changes. @@ -22,10 +24,12 @@ class MessageUpdateJob extends GenericTranslateJob { * Create a normal message update job without a rename process * @param Title $target * @param string $content - * @param bool $fuzzy - * @return MessageUpdateJob + * @param string|false $fuzzy + * @return self */ - public static function newJob( Title $target, $content, $fuzzy = false ) { + public static function newJob( + Title $target, string $content, $fuzzy = false + ): self { $params = [ 'content' => $content, 'fuzzy' => $fuzzy, @@ -38,17 +42,22 @@ class MessageUpdateJob extends GenericTranslateJob { /** * Create a message update job containing a rename process - * @param Title $target Target message being modified - * @param string $targetStr Target string - * @param string $replacement Replacement string - * @param bool $fuzzy Whether to fuzzy the message - * @param string $content Content of the source language - * @param array $otherLangContents Content to be updated for other languages - * @return MessageUpdateJob + * @param Title $target + * @param string $targetStr + * @param string $replacement + * @param string|false $fuzzy + * @param string $content + * @param array $otherLangContents + * @return self */ public static function newRenameJob( - Title $target, $targetStr, $replacement, $fuzzy, $content, $otherLangContents = [] - ) { + Title $target, + string $targetStr, + string $replacement, + $fuzzy, + string $content, + array $otherLangContents = [] + ): self { $params = [ 'target' => $targetStr, 'replacement' => $replacement, @@ -78,6 +87,7 @@ class MessageUpdateJob extends GenericTranslateJob { $isRename = $params['rename'] ?? false; $isFuzzy = $params['fuzzy'] ?? false; $otherLangs = $params['otherLangs'] ?? []; + $originalTitle = Title::newFromLinkTarget( $this->title->getTitleValue(), Title::NEW_CLONE ); if ( $isRename ) { $this->title = $this->handleRename( $params['target'], $params['replacement'], $user ); @@ -90,6 +100,8 @@ class MessageUpdateJob extends GenericTranslateJob { 'target' => $params['target'] ] ); + + $this->removeFromCache( $originalTitle ); return true; } } @@ -121,6 +133,7 @@ class MessageUpdateJob extends GenericTranslateJob { $this->handleFuzzy( $title ); } + $this->removeFromCache( $originalTitle ); return true; } @@ -152,16 +165,10 @@ class MessageUpdateJob extends GenericTranslateJob { $renameSummary = wfMessage( 'translate-manage-import-rename-summary' ) ->inContentLanguage()->plain(); - /** - * @var Title[] $movableTitles - */ - foreach ( $movableTitles as $mTitle ) { - /** - * @var Title $sourceTitle - * @var Title $replacementTitle - */ - [ $sourceTitle, $replacementTitle ] = $mTitle; - $mv = new MovePage( $sourceTitle, $replacementTitle ); + foreach ( $movableTitles as [ $sourceTitle, $replacementTitle ] ) { + $mv = MediaWikiServices::getInstance() + ->getMovePageFactory() + ->newMovePage( $sourceTitle, $replacementTitle ); $status = $mv->move( $user, $renameSummary, false ); if ( !$status->isOK() ) { @@ -278,4 +285,48 @@ class MessageUpdateJob extends GenericTranslateJob { } } } + + private function removeFromCache( Title $title ): void { + $config = MediaWikiServices::getInstance()->getMainConfig(); + + if ( !$config->get( 'TranslateGroupSynchronizationCache' ) ) { + return; + } + + $currentTitle = $title; + // Check if the current title, is equal to the title passed. This condition will be + // true incase of rename where the old title would have been renamed. + if ( $this->title && $this->title->getPrefixedDBkey() !== $title->getPrefixedDBkey() ) { + $currentTitle = $this->title; + } + + $sourceMessageHandle = new MessageHandle( $currentTitle ); + $groupIds = $sourceMessageHandle->getGroupIds(); + if ( !$groupIds ) { + $this->logWarning( + 'Could not find group Id for message title', + $this->getParams() + ); + return; + } + + $groupId = $groupIds[0]; + $group = MessageGroups::getGroup( $groupId ); + + if ( !$group instanceof FileBasedMessageGroup ) { + return; + } + + $groupSyncCache = Services::getInstance()->getGroupSynchronizationCache(); + $messageKey = $title->getPrefixedDBkey(); + + if ( $groupSyncCache->isMessageBeingProcessed( $groupId, $messageKey ) ) { + $groupSyncCache->removeMessages( $groupId, $messageKey ); + } else { + $this->logWarning( + "Did not find key: $messageKey; in group: $groupId in group sync cache", + $this->getParams() + ); + } + } } diff --git a/MLEB/Translate/utils/MessageWebImporter.php b/MLEB/Translate/utils/MessageWebImporter.php index 308eba62..2ba543c9 100644 --- a/MLEB/Translate/utils/MessageWebImporter.php +++ b/MLEB/Translate/utils/MessageWebImporter.php @@ -10,7 +10,7 @@ * @license GPL-2.0-or-later */ -use MediaWiki\Extensions\Translate\SystemUsers\FuzzyBot; +use MediaWiki\Extension\Translate\SystemUsers\FuzzyBot; use MediaWiki\MediaWikiServices; use MediaWiki\Revision\SlotRecord; @@ -19,28 +19,16 @@ use MediaWiki\Revision\SlotRecord; * displays them in pretty way with diffs and finally executes the actions the user choices. */ class MessageWebImporter { - /** - * @var Title - */ + /** @var Title */ protected $title; - - /** - * @var User - */ + /** @var User */ protected $user; - - /** - * @var MessageGroup - */ + /** @var MessageGroup */ protected $group; protected $code; protected $time; - - /** - * @var OutputPage - */ + /** @var OutputPage */ protected $out; - /** * Maximum processing time in seconds. */ @@ -66,30 +54,22 @@ class MessageWebImporter { return $this->title; } - /** - * @param Title $title - */ + /** @param Title $title */ public function setTitle( Title $title ) { $this->title = $title; } - /** - * @return User - */ + /** @return User */ public function getUser() { return $this->user ?: RequestContext::getMain()->getUser(); } - /** - * @param User $user - */ + /** @param User $user */ public function setUser( User $user ) { $this->user = $user; } - /** - * @return MessageGroup - */ + /** @return MessageGroup */ public function getGroup() { return $this->group; } @@ -106,30 +86,22 @@ class MessageWebImporter { } } - /** - * @return string - */ + /** @return string */ public function getCode() { return $this->code; } - /** - * @param string $code - */ + /** @param string $code */ public function setCode( $code = 'en' ) { $this->code = $code; } - /** - * @return string - */ + /** @return string */ protected function getAction() { return $this->getTitle()->getFullURL(); } - /** - * @return string - */ + /** @return string */ protected function doHeader() { $formParams = [ 'method' => 'post', @@ -143,16 +115,12 @@ class MessageWebImporter { Html::hidden( 'process', 1 ); } - /** - * @return string - */ + /** @return string */ protected function doFooter() { return '</form>'; } - /** - * @return bool - */ + /** @return bool */ protected function allowProcess() { $request = RequestContext::getMain()->getRequest(); @@ -161,9 +129,7 @@ class MessageWebImporter { && $this->getUser()->matchEditToken( $request->getVal( 'token' ) ); } - /** - * @return array - */ + /** @return array */ protected function getActions() { if ( $this->code === 'en' ) { return [ 'import', 'fuzzy', 'ignore' ]; @@ -393,7 +359,7 @@ class MessageWebImporter { * Perform an action on a given group/key/code * * @param string $action Options: 'import', 'conflict' or 'ignore' - * @param MessageGroup $group Group object + * @param MessageGroup $group * @param string $key Message key * @param string $code Language code * @param string $message Contents for the $key/code combination @@ -507,10 +473,7 @@ class MessageWebImporter { ); $changed = []; - $slots = []; - if ( is_callable( [ $revStore, 'getContentBlobsForBatch' ] ) ) { - $slots = $revStore->getContentBlobsForBatch( $rows, [ SlotRecord::MAIN ] )->getValue(); - } + $slots = $revStore->getContentBlobsForBatch( $rows, [ SlotRecord::MAIN ] )->getValue(); foreach ( $rows as $row ) { global $wgTranslateDocumentationLanguageCode; diff --git a/MLEB/Translate/utils/StatsBar.php b/MLEB/Translate/utils/StatsBar.php index f663e84f..4a6aaef9 100644 --- a/MLEB/Translate/utils/StatsBar.php +++ b/MLEB/Translate/utils/StatsBar.php @@ -18,15 +18,9 @@ class StatsBar { * @var int[] */ protected $stats; - - /** - * @var string Message group id - */ + /** @var string Message group id */ protected $group; - - /** - * @var string Language - */ + /** @var string Language */ protected $language; /** diff --git a/MLEB/Translate/utils/StatsTable.php b/MLEB/Translate/utils/StatsTable.php index 03b9d73f..46493683 100644 --- a/MLEB/Translate/utils/StatsTable.php +++ b/MLEB/Translate/utils/StatsTable.php @@ -15,24 +15,13 @@ * @ingroup Stats */ class StatsTable { - /** - * @var Language - */ + /** @var Language */ protected $lang; - - /** - * @var Title - */ + /** @var Title */ protected $translate; - - /** - * @var string - */ + /** @var string */ protected $mainColumnHeader; - - /** - * @var Message[] - */ + /** @var Message[] */ protected $extraColumns = []; public function __construct() { @@ -88,16 +77,12 @@ class StatsTable { return $colors[ $index ]; } - /** - * @return string - */ + /** @return string */ public function getMainColumnHeader() { return $this->mainColumnHeader; } - /** - * @param Message $msg - */ + /** @param Message $msg */ public function setMainColumnHeader( Message $msg ) { $this->mainColumnHeader = $this->createColumnHeader( $msg ); } @@ -114,9 +99,7 @@ class StatsTable { $this->extraColumns[] = $column; } - /** - * @return Message[] - */ + /** @return Message[] */ public function getOtherColumnHeaders() { return array_merge( [ wfMessage( 'translate-total' ), @@ -127,9 +110,7 @@ class StatsTable { ], $this->extraColumns ); } - /** - * @return string HTML - */ + /** @return string HTML */ public function createHeader() { // Create table header $out = Html::openElement( @@ -276,10 +257,10 @@ class StatsTable { * @param string $code Language code * @return bool */ - public function isBlacklisted( $groupId, $code ) { + public function isExcluded( $groupId, $code ) { global $wgTranslateBlacklist; - $blacklisted = null; + $excluded = null; $checks = [ $groupId, @@ -289,10 +270,10 @@ class StatsTable { foreach ( $checks as $check ) { if ( isset( $wgTranslateBlacklist[$check] ) && isset( $wgTranslateBlacklist[$check][$code] ) ) { - $blacklisted = $wgTranslateBlacklist[$check][$code]; + $excluded = $wgTranslateBlacklist[$check][$code]; } - if ( $blacklisted !== null ) { + if ( $excluded !== null ) { break; } } @@ -300,32 +281,14 @@ class StatsTable { $group = MessageGroups::getGroup( $groupId ); $languages = $group->getTranslatableLanguages(); if ( $languages !== null && !isset( $languages[$code] ) ) { - $blacklisted = true; + $excluded = true; } $include = Hooks::run( 'Translate:MessageGroupStats:isIncluded', [ $groupId, $code ] ); if ( !$include ) { - $blacklisted = true; + $excluded = true; } - return $blacklisted; - } - - /** - * Used to circumvent ugly tooltips when newlines are used in the - * message content ("x\ny" becomes "x y"). - * @param string $text - * @return string - */ - public static function formatTooltip( $text ) { - $wordSeparator = wfMessage( 'word-separator' )->text(); - - $text = strtr( $text, [ - "\n" => $wordSeparator, - "\r" => $wordSeparator, - "\t" => $wordSeparator, - ] ); - - return $text; + return $excluded; } } diff --git a/MLEB/Translate/utils/TranslateLogFormatter.php b/MLEB/Translate/utils/TranslateLogFormatter.php index 39a1243e..24049de4 100644 --- a/MLEB/Translate/utils/TranslateLogFormatter.php +++ b/MLEB/Translate/utils/TranslateLogFormatter.php @@ -63,6 +63,13 @@ class TranslateLogFormatter extends LogFormatter { return $message->isBlank() ? $value : $message->text(); } + /** + * @param Title|null $title The page + * @param string|null $text + * @param array $parameters Query parameters + * @return string + * @return-taint onlysafefor_html + */ protected function makePageLinkWithText( ?Title $title, $text, array $parameters = [] ) { diff --git a/MLEB/Translate/utils/TranslateMetadata.php b/MLEB/Translate/utils/TranslateMetadata.php index c6d0027e..a0cd25f6 100644 --- a/MLEB/Translate/utils/TranslateMetadata.php +++ b/MLEB/Translate/utils/TranslateMetadata.php @@ -14,9 +14,7 @@ class TranslateMetadata { /** @var array Map of (group => key => value) */ private static $cache = []; - /** - * @param string[] $groups List of translate groups - */ + /** @param string[] $groups List of translate groups */ public static function preloadGroups( array $groups ) { $missing = array_keys( array_diff_key( array_flip( $groups ), self::$cache ) ); if ( !$missing ) { @@ -93,12 +91,12 @@ class TranslateMetadata { /** * Wrapper for getting subgroups. * @param string $groupId - * @return string[]|bool + * @return string[]|null * @since 2012-05-09 */ - public static function getSubgroups( $groupId ) { + public static function getSubgroups( string $groupId ): ?array { $groups = self::get( $groupId, 'subgroups' ); - if ( $groups !== false ) { + if ( is_string( $groups ) ) { if ( strpos( $groups, '|' ) !== false ) { $groups = explode( '|', $groups ); } else { @@ -110,6 +108,8 @@ class TranslateMetadata { unset( $groups[$index] ); } } + } else { + $groups = null; } return $groups; diff --git a/MLEB/Translate/utils/TranslateSandbox.php b/MLEB/Translate/utils/TranslateSandbox.php index 0ed2ea18..125c5afb 100644 --- a/MLEB/Translate/utils/TranslateSandbox.php +++ b/MLEB/Translate/utils/TranslateSandbox.php @@ -10,7 +10,7 @@ use MediaWiki\Auth\AuthenticationRequest; use MediaWiki\Auth\AuthenticationResponse; use MediaWiki\Auth\AuthManager; -use MediaWiki\Extensions\Translate\SystemUsers\TranslateUserManager; +use MediaWiki\Extension\Translate\SystemUsers\TranslateUserManager; use MediaWiki\MediaWikiServices; use Wikimedia\ScopedCallback; @@ -19,8 +19,6 @@ use Wikimedia\ScopedCallback; * lot of assumptions about what happens to the user account. */ class TranslateSandbox { - public static $userToCreate = null; - /** * Adds a new user without doing much validation. * @@ -33,7 +31,7 @@ class TranslateSandbox { public static function addUser( $name, $email, $password ) { $user = User::newFromName( $name, 'creatable' ); - if ( !$user instanceof User ) { + if ( !$user ) { throw new MWException( 'Invalid user name' ); } @@ -83,7 +81,12 @@ class TranslateSandbox { } // group-translate-sandboxed group-translate-sandboxed-member - $user->addGroup( 'translate-sandboxed' ); + if ( method_exists( MediaWikiServices::class, 'getUserGroupManager' ) ) { + // MediaWiki 1.35+ + MediaWikiServices::getInstance()->getUserGroupManager()->addUserToGroup( $user, 'translate-sandboxed' ); + } else { + $user->addGroup( 'translate-sandboxed' ); + } return $user; } @@ -97,6 +100,7 @@ class TranslateSandbox { */ public static function deleteUser( User $user, $force = '' ) { $uid = $user->getId(); + $actorId = $user->getActorId(); if ( $force !== 'force' && !self::isSandboxed( $user ) ) { throw new MWException( 'Not a sandboxed user' ); @@ -108,11 +112,10 @@ class TranslateSandbox { $dbw->delete( 'user_groups', [ 'ug_user' => $uid ], __METHOD__ ); $dbw->delete( 'user_properties', [ 'up_user' => $uid ], __METHOD__ ); - $m = ActorMigration::newMigration(); $dbw->delete( 'actor', [ 'actor_user' => $uid ], __METHOD__ ); // Assume no joins are needed for logging or recentchanges - $dbw->delete( 'logging', $m->getWhere( $dbw, 'log_user', $user )['conds'], __METHOD__ ); - $dbw->delete( 'recentchanges', $m->getWhere( $dbw, 'rc_user', $user )['conds'], __METHOD__ ); + $dbw->delete( 'logging', [ 'log_actor' => $actorId ], __METHOD__ ); + $dbw->delete( 'recentchanges', [ 'rc_actor' => $actorId ], __METHOD__ ); // Update the site stats $statsUpdate = SiteStatsUpdate::factory( [ 'users' => -1 ] ); @@ -170,13 +173,33 @@ class TranslateSandbox { throw new MWException( 'Not a sandboxed user' ); } - $user->removeGroup( 'translate-sandboxed' ); - if ( $wgTranslateSandboxPromotedGroup ) { - $user->addGroup( $wgTranslateSandboxPromotedGroup ); + $services = MediaWikiServices::getInstance(); + + if ( method_exists( $services, 'getUserGroupManager' ) ) { + // MediaWiki 1.35+ + $userGroupManager = $services->getUserGroupManager(); + $userGroupManager->removeUserFromGroup( $user, 'translate-sandboxed' ); + + if ( $wgTranslateSandboxPromotedGroup ) { + $userGroupManager->addUserToGroup( $user, $wgTranslateSandboxPromotedGroup ); + } + } else { + $user->removeGroup( 'translate-sandboxed' ); + + if ( $wgTranslateSandboxPromotedGroup ) { + $user->addGroup( $wgTranslateSandboxPromotedGroup ); + } } - $user->setOption( 'translate-sandbox-reminders', '' ); - $user->saveSettings(); + if ( method_exists( $services, 'getUserOptionsManager' ) ) { + // MW 1.35+ + $userOptionsManager = $services->getUserOptionsManager(); + $userOptionsManager->setOption( $user, 'translate-sandbox-reminders', '' ); + $userOptionsManager->saveOptions( $user ); + } else { + $user->setOption( 'translate-sandbox-reminders', '' ); + $user->saveSettings(); + } } /** @@ -247,11 +270,15 @@ class TranslateSandbox { * @since 2013.06 */ public static function isSandboxed( User $user ) { - if ( in_array( 'translate-sandboxed', $user->getGroups(), true ) ) { - return true; + if ( method_exists( MediaWikiServices::class, 'getUserGroupManager' ) ) { + // MediaWiki 1.35+ + $userGroupManager = MediaWikiServices::getInstance()->getUserGroupManager(); + $groups = $userGroupManager->getUserGroups( $user ); + } else { + $groups = $user->getGroups(); } - return false; + return in_array( 'translate-sandboxed', $groups, true ); } /** @@ -295,7 +322,7 @@ class TranslateSandbox { } /** - * Whitelisting for certain API modules. See also enforcePermissions. + * Inclusion listing for certain API modules. See also enforcePermissions. * Hook: ApiCheckCanExecute * @param ApiBase $module * @param User $user @@ -303,7 +330,7 @@ class TranslateSandbox { * @return bool */ public static function onApiCheckCanExecute( ApiBase $module, User $user, &$message ) { - $whitelist = [ + $inclusionList = [ // Obviously this is needed to get out of the sandbox 'ApiTranslationStash', // Used by UniversalLanguageSelector for example @@ -312,7 +339,7 @@ class TranslateSandbox { if ( self::isSandboxed( $user ) ) { $class = get_class( $module ); - if ( $module->isWriteMode() && !in_array( $class, $whitelist, true ) ) { + if ( $module->isWriteMode() && !in_array( $class, $inclusionList, true ) ) { $message = ApiMessage::create( 'apierror-writeapidenied' ); return false; } diff --git a/MLEB/Translate/utils/TranslateSandboxEmailJob.php b/MLEB/Translate/utils/TranslateSandboxEmailJob.php index 4c3716b9..1c078e6f 100644 --- a/MLEB/Translate/utils/TranslateSandboxEmailJob.php +++ b/MLEB/Translate/utils/TranslateSandboxEmailJob.php @@ -51,11 +51,16 @@ class TranslateSandboxEmailJob extends Job { $reminders = $user->getOption( 'translate-sandbox-reminders' ); $reminders = $reminders ? explode( '|', $reminders ) : []; $reminders[] = wfTimestamp(); - $user->setOption( 'translate-sandbox-reminders', implode( '|', $reminders ) ); - $reminders = $user->getOption( 'translate-sandbox-reminders' ); - $user->setOption( 'translate-sandbox-reminders', $reminders ); - $user->saveSettings(); + if ( method_exists( $services, 'getUserOptionsManager' ) ) { + // MW 1.35+ + $userOptionsManager = $services->getUserOptionsManager(); + $userOptionsManager->setOption( $user, 'translate-sandbox-reminders', implode( '|', $reminders ) ); + $userOptionsManager->saveOptions( $user ); + } else { + $user->setOption( 'translate-sandbox-reminders', implode( '|', $reminders ) ); + $user->saveSettings(); + } } return $isOK; diff --git a/MLEB/Translate/utils/TranslateToolbox.php b/MLEB/Translate/utils/TranslateToolbox.php index 0836f485..9503f3ab 100644 --- a/MLEB/Translate/utils/TranslateToolbox.php +++ b/MLEB/Translate/utils/TranslateToolbox.php @@ -48,7 +48,7 @@ class TranslateToolbox { /** * This handler will be called for MW >= 1.35 * - * @param Skin $skin The skin + * @param Skin $skin * @param array &$sidebar Array with sidebar items * * @return void diff --git a/MLEB/Translate/utils/TranslationHelpers.php b/MLEB/Translate/utils/TranslationHelpers.php index 9b585c72..38ee5acb 100644 --- a/MLEB/Translate/utils/TranslationHelpers.php +++ b/MLEB/Translate/utils/TranslationHelpers.php @@ -19,24 +19,18 @@ class TranslationHelpers { * @since 2012-01-04 */ protected $handle; - - /** - * @var TranslationAidDataProvider - */ + /** @var TranslationAidDataProvider */ private $dataProvider; - /** * The group object of the message (or null if there isn't any) * @var MessageGroup|null */ protected $group; - /** * The current translation. * @var string */ private $translation; - /** * HTML id to the text area that contains the translation. Used to insert * suggestion directly into the text area, for example. @@ -79,21 +73,13 @@ class TranslationHelpers { /** * Gets the HTML id of the text area that contains the translation. - * @return String + * @return string */ public function getTextareaId() { return $this->textareaId; } /** - * Sets the HTML id of the text area that contains the translation. - * @param string $id - */ - public function setTextareaId( $id ) { - $this->textareaId = $id; - } - - /** * Enable or disable extra help for editing. * @param bool $mode */ @@ -152,29 +138,10 @@ class TranslationHelpers { } /** - * Gets the linguistically correct language code for translation - * @return string - */ - public function getTargetLanguage() { - global $wgLanguageCode, $wgTranslateDocumentationLanguageCode; - - $code = $this->handle->getCode(); - if ( !$code ) { - $this->mustBeKnownMessage(); - $code = $this->group->getSourceLanguage(); - } - if ( $code === $wgTranslateDocumentationLanguageCode ) { - return $wgLanguageCode; - } - - return $code; - } - - /** * Returns block element HTML snippet that contains the translation aids. * Not all boxes are shown all the time depending on whether they have * any information to show and on configuration variables. - * @return String Block level HTML snippet or empty string. + * @return string Block level HTML snippet or empty string. */ public function getBoxes() { // Box filter @@ -218,13 +185,8 @@ class TranslationHelpers { } } - /** - * @return array - */ - public function getBoxNames() { + public function getBoxNames(): array { return [ - 'other-languages' => [ $this, 'getOtherLanguagesBox' ], - 'separator' => [ $this, 'getSeparatorBox' ], 'documentation' => [ $this, 'getDocumentationBox' ], 'definition' => [ $this, 'getDefinitionBox' ], ]; @@ -234,20 +196,11 @@ class TranslationHelpers { $this->mustHaveDefinition(); $en = $this->getDefinition(); - $title = Linker::link( - SpecialPage::getTitleFor( 'Translate' ), - htmlspecialchars( $this->group->getLabel() ), - [], - [ - 'group' => $this->group->getId(), - 'language' => $this->handle->getCode() - ] - ); - + $linkTag = self::ajaxEditLink( $this->handle->getTitle(), $this->group->getLabel() ); $label = wfMessage( 'translate-edit-definition' )->escaped() . wfMessage( 'word-separator' )->escaped() . - wfMessage( 'parentheses' )->rawParams( $title )->escaped(); + wfMessage( 'parentheses' )->rawParams( $linkTag )->escaped(); // Source language object $sl = Language::factory( $this->group->getSourceLanguage() ); @@ -265,89 +218,11 @@ class TranslationHelpers { $msg .= $this->wrapInsert( $id, $en ); - $class = [ 'class' => 'mw-sp-translate-edit-definition mw-translate-edit-definition' ]; - - return TranslateUtils::fieldset( $label, $msg, $class ); - } - - public function getTranslationDisplayBox() { - $en = $this->getTranslation(); - if ( $en === null ) { - return null; - } - $label = wfMessage( 'translate-edit-translation' )->escaped(); - $class = [ 'class' => 'mw-translate-edit-translation' ]; - $msg = Html::rawElement( 'span', - [ 'class' => 'mw-translate-edit-translationtext' ], - TranslateUtils::convertWhiteSpaceToHTML( $en ) - ); + $class = [ 'class' => 'mw-sp-translate-edit-definition' ]; return TranslateUtils::fieldset( $label, $msg, $class ); } - public function getOtherLanguagesBox() { - $code = $this->handle->getCode(); - $page = $this->handle->getKey(); - $ns = $this->handle->getTitle()->getNamespace(); - - $boxes = []; - foreach ( self::getFallbacks( $code ) as $fbcode ) { - $text = TranslateUtils::getMessageContent( $page, $fbcode, $ns ); - if ( $text === null ) { - continue; - } - - $fbLanguage = Language::factory( $fbcode ); - $context = RequestContext::getMain(); - $label = TranslateUtils::getLanguageName( $fbcode, $context->getLanguage()->getCode() ) . - $context->msg( 'word-separator' )->text() . - $context->msg( 'parentheses', $fbLanguage->getHtmlCode() )->text(); - - $target = $this->handle->getTitleForLanguage( $fbcode ); - - if ( $target ) { - $label = self::ajaxEditLink( $target, $label ); - } - - $dialogID = $this->dialogID(); - $id = Sanitizer::escapeIdForAttribute( "other-$fbcode-$dialogID" ); - - $params = [ 'class' => 'mw-translate-edit-item' ]; - - $display = TranslateUtils::convertWhiteSpaceToHTML( $text ); - $display = Html::rawElement( 'div', [ - 'lang' => $fbLanguage->getHtmlCode(), - 'dir' => $fbLanguage->getDir() ], - $display - ); - - $contents = self::legend( $label ) . "\n" . $this->adder( $id, $fbLanguage ) . - $display . self::clear(); - - $boxes[] = Html::rawElement( 'div', $params, $contents ) . - $this->wrapInsert( $id, $text ); - } - - if ( count( $boxes ) ) { - $sep = Html::element( 'hr', [ 'class' => 'mw-translate-sep' ] ); - - return TranslateUtils::fieldset( - wfMessage( - 'translate-edit-in-other-languages', - $page - )->escaped(), - implode( "$sep\n", $boxes ), - [ 'class' => 'mw-sp-translate-edit-inother' ] - ); - } - - return null; - } - - public function getSeparatorBox() { - return Html::element( 'div', [ 'class' => 'mw-translate-edit-extra' ] ); - } - public function getDocumentationBox() { global $wgTranslateDocumentationLanguageCode; @@ -388,60 +263,7 @@ class TranslationHelpers { ); } - /** - * @param string $label - * @return string - */ - protected static function legend( $label ) { - # Float it to the opposite direction - return Html::rawElement( 'div', [ 'class' => 'mw-translate-legend' ], $label ); - } - - /** - * @return string - */ - protected static function clear() { - return Html::element( 'div', [ 'style' => 'clear:both;' ] ); - } - - /** - * @param string $code - * @return array - */ - protected static function getFallbacks( $code ) { - global $wgTranslateLanguageFallbacks; - - // User preference has the final say - $user = RequestContext::getMain()->getUser(); - $preference = $user->getOption( 'translate-editlangs' ); - if ( $preference !== 'default' ) { - $fallbacks = array_map( 'trim', explode( ',', $preference ) ); - foreach ( $fallbacks as $k => $v ) { - if ( $v === $code ) { - unset( $fallbacks[$k] ); - } - } - - return $fallbacks; - } - - // Global configuration settings - $fallbacks = []; - if ( isset( $wgTranslateLanguageFallbacks[$code] ) ) { - $fallbacks = (array)$wgTranslateLanguageFallbacks[$code]; - } - - $list = Language::getFallbacksFor( $code ); - array_pop( $list ); // Get 'en' away from the end - $fallbacks = array_merge( $list, $fallbacks ); - - return array_unique( $fallbacks ); - } - - /** - * @return string - */ - public function dialogID() { + public function dialogID(): string { $hash = sha1( $this->handle->getTitle()->getPrefixedDBkey() ); return substr( $hash, 0, 4 ); @@ -449,7 +271,7 @@ class TranslationHelpers { /** * @param string $source jQuery selector for element containing the source - * @param Language $lang Language object + * @param Language $lang * @return string */ public function adder( $source, $lang ) { |