00001 <?php
00012
00013 if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
00014 $IP = getenv( 'MW_INSTALL_PATH' );
00015 } else {
00016 $dir = __DIR__;
00017 $IP = "$dir/../../..";
00018 }
00019 require_once "$IP/maintenance/Maintenance.php";
00020
00030 class ProcessMessageChanges extends Maintenance {
00031 protected $changes = array();
00032
00036 protected $counter;
00037
00038 public function __construct() {
00039 parent::__construct();
00040 $this->mDescription = 'Script for processing message changes in file based message groups';
00041 $this->addOption(
00042 'group',
00043 '(optional) Comma separated list of group IDs to process (can use * as wildcard). ' .
00044 'Default: "*"',
00045 false,
00046 true
00047 );
00048 $this->addOption(
00049 'skipgroup',
00050 '(optional) Comma separated list of group IDs to not process (can use * ' .
00051 'as wildcard). Overrides --group parameter.',
00052 false,
00053 true
00054 );
00055 }
00056
00057 public function execute() {
00058 $groups = $this->getGroups();
00059
00060 $this->counter = 0;
00062 foreach ( $groups as $id => $group ) {
00063 $this->output( "Processing $id\n" );
00064 $this->processMessageGroup( $group );
00065 if ( $this->counter > 25000 ) {
00066 $this->output( "Too many changes. Rerun this script after processing current changes\n" );
00067 break;
00068 }
00069 }
00070 if ( count( $this->changes ) ) {
00071 $this->writeChanges();
00072 $this->output( "Process changes with Special:ManageMessageGroups\n" );
00073 } else {
00074 $this->output( "No changes found\n" );
00075 }
00076 }
00077
00082 protected function getGroups() {
00084 $groups = MessageGroups::getGroupsByType( 'FileBasedMessageGroup' );
00085
00086
00087 $include = $this->getOption( 'group', '*' );
00088 $include = explode( ',', $include );
00089 $include = array_map( 'trim', $include );
00090 $include = MessageGroups::expandWildcards( $include );
00091
00092
00093 $exclude = $this->getOption( 'skipgroup', '' );
00094 $exclude = explode( ',', $exclude );
00095 $exclude = array_map( 'trim', $exclude );
00096 $exclude = MessageGroups::expandWildcards( $exclude );
00097
00098
00099 $include = array_flip( $include );
00100 $exclude = array_flip( $exclude );
00101
00102 $groups = array_filter( $groups,
00103 function ( MessageGroup $group ) use ( $include, $exclude ) {
00104 $id = $group->getId();
00105
00106 return isset( $include[$id] ) && !isset( $exclude[$id] );
00107 }
00108 );
00109
00110 return $groups;
00111 }
00112
00113 protected function writeChanges() {
00114
00115 wfProfileIn( __METHOD__ );
00116 $array = $this->changes;
00117
00118
00119
00120 $file = TranslateUtils::cacheFile( SpecialManageGroups::CHANGEFILE );
00121 $cache = CdbWriter::open( $file );
00122 $keys = array_keys( $array );
00123 $cache->set( '#keys', serialize( $keys ) );
00124
00125 foreach ( $array as $key => $value ) {
00126 $value = serialize( $value );
00127 $cache->set( $key, $value );
00128 }
00129 $cache->close();
00130 wfProfileOut( __METHOD__ );
00131 }
00132
00133 protected function processMessageGroup( FileBasedMessageGroup $group ) {
00134 $languages = Language::getLanguageNames( false );
00135
00136
00137 $sourceLanguage = $group->getSourceLanguage();
00138 unset( $languages[$sourceLanguage] );
00139 $languages = array_keys( $languages );
00140 $this->processLanguage( $group, $sourceLanguage );
00141
00142 foreach ( $languages as $code ) {
00143 $this->processLanguage( $group, $code );
00144 }
00145 }
00146
00147 protected function processLanguage( FileBasedMessageGroup $group, $code ) {
00148 wfProfileIn( __METHOD__ );
00149 $cache = new MessageGroupCache( $group, $code );
00150 $reason = 0;
00151 if ( !$cache->isValid( $reason ) ) {
00152 $this->addMessageUpdateChanges( $group, $code, $reason, $cache );
00153
00154 if ( !isset( $this->changes[$group->getId()][$code] ) ) {
00155
00156
00157
00158 $cache->create();
00159 }
00160 }
00161 wfProfileOut( __METHOD__ );
00162 }
00163
00180 protected function addMessageUpdateChanges( FileBasedMessageGroup $group, $code,
00181 $reason, $cache
00182 ) {
00183 wfProfileIn( __METHOD__ );
00184
00185
00186 wfSuppressWarnings();
00187 $wiki = $group->initCollection( $code );
00188 wfRestoreWarnings();
00189 $wiki->filter( 'hastranslation', false );
00190 $wiki->loadTranslations();
00191 $wikiKeys = $wiki->getMessageKeys();
00192
00193
00194 $ffs = $group->getFFS();
00195 if ( $code === $group->getSourceLanguage() && !$ffs->exists( $code ) ) {
00196 $path = $group->getSourceFilePath( $code );
00197 $this->error( "Source message file for {$group->getId()} does not exist. Looking for $path", 1 );
00198 }
00199 $file = $ffs->read( $code );
00200 if ( !isset( $file['MESSAGES'] ) ) {
00201 error_log( "{$group->getId()} has an FFS - the FFS didn't return cake for $code" );
00202 }
00203 $fileKeys = array_keys( $file['MESSAGES'] );
00204
00205 $common = array_intersect( $fileKeys, $wikiKeys );
00206
00207 $supportsFuzzy = $ffs->supportsFuzzy();
00208
00209 foreach ( $common as $key ) {
00210 $sourceContent = $file['MESSAGES'][$key];
00211 $wikiContent = $wiki[$key]->translation();
00212
00213
00214 $wikiContent = str_replace( TRANSLATE_FUZZY, '', $wikiContent );
00215
00216 if ( $supportsFuzzy === 'yes' && $wiki[$key]->hasTag( 'fuzzy' ) ) {
00217 $wikiContent = TRANSLATE_FUZZY . $wikiContent;
00218 }
00219
00220 if ( self::compareContent( $sourceContent, $wikiContent ) ) {
00221
00222 continue;
00223 }
00224
00225
00226
00227
00228 if ( $reason !== MessageGroupCache::NO_CACHE ) {
00229 $cacheContent = $cache->get( $key );
00230
00231
00232
00233
00234
00235 if (
00236 !self::compareContent( $wikiContent, $cacheContent ) &&
00237 self::compareContent( $sourceContent, $cacheContent )
00238 ) {
00239 continue;
00240 }
00241 }
00242
00243 $this->addChange( 'change', $group, $code, $key, $sourceContent );
00244 }
00245
00246 $added = array_diff( $fileKeys, $wikiKeys );
00247 foreach ( $added as $key ) {
00248 $sourceContent = $file['MESSAGES'][$key];
00249 if ( trim( $sourceContent ) === '' ) {
00250 continue;
00251 }
00252 $this->addChange( 'addition', $group, $code, $key, $sourceContent );
00253 }
00254
00255
00256
00257
00258
00259 if ( $reason !== MessageGroupCache::NO_CACHE ) {
00260 $deleted = array_diff( $wikiKeys, $fileKeys );
00261 foreach ( $deleted as $key ) {
00262 if ( $cache->get( $key ) === false ) {
00263
00264
00265 continue;
00266 }
00267 $this->addChange( 'deletion', $group, $code, $key, null );
00268 }
00269 }
00270
00271 wfProfileOut( __METHOD__ );
00272 }
00273
00274 protected function addChange( $type, $group, $language, $key, $content ) {
00275 $this->counter++;
00276 $this->changes[$group->getId()][$language][$type][] = array(
00277 'key' => $key,
00278 'content' => $content,
00279 );
00280 }
00281
00289 protected static function compareContent( $a, $b ) {
00290 return $a === $b;
00291 }
00292 }
00293
00294 $maintClass = 'ProcessMessageChanges';
00295 require_once RUN_MAINTENANCE_IF_MAIN;