Nikerabbit has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/118446

Change subject: Rewritten update script and configuration
......................................................................

Rewritten update script and configuration

LU rewrite part 5: The update.php was rewritten to be a maintenance
class and use the new helper classes. New configuration format for
repositories, which is not backwards compatible. Will use GitHub
as default as it provides list of files in a directory which is
needed for efficient fetching of json translations.

Cleaned up LocalisationUpdate class of now unused code.

Speed seems reasonable. With translatewiki.net like setup, speeds
were as following:

Local clones:
Saving 2866 new translations

real    1m26.045s
user    1m25.149s
sys     0m0.688s

From GitHub
Saving 2866 new translations

real    5m4.899s
user    1m38.182s
sys     0m1.788s

Updates could be further optimized by not fetching files which have not
changed a) since last update b) from what is currently in use. Fix a)
would need to require for us to store the updated translations per file.

This extension fully supports json in the following ways:
1) Extensions using json files are supported, picking up new languages
as they appear.
2) Core is easy to switch to json once it has been converted.
3) The extension itself stores updated translations in json format.

There are few caveats though
4) Once core switches to json format, all users must update LU to keep
receiving updates (but they will need to update anyway because older
version of this extension does not support json at all)
5) If extension switches to json, but the extension is not updated
locally to such version, we will not be able to provide updates as this
extension does not know that and will try to fetching the PHP shim which
does not have translations any longer. Updates will be resumed once those
versions have been updated locally to version which uses json files (which
is usually okay, as the PHP shims supports old MediaWiki versions; problems
might appear if the extension has other incompatible changes which prevent
updating)

I have tried to written this extension in a way that it is compatible with
MediaWiki 1.19 which includes being compatible with PHP 5.2.3. After support
for that version is dropped, some code can be improved to take advantages of
new features in PHP 5.3, mainly namespaces.

Change-Id: I21ce94ae2262d9c90132d21c4c9f8ebdd08a14ea
---
M LocalisationUpdate.class.php
M LocalisationUpdate.php
M update.php
3 files changed, 118 insertions(+), 666 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/LocalisationUpdate 
refs/changes/46/118446/1

diff --git a/LocalisationUpdate.class.php b/LocalisationUpdate.class.php
index 2462054..dadc3eb 100644
--- a/LocalisationUpdate.class.php
+++ b/LocalisationUpdate.class.php
@@ -1,651 +1,55 @@
 <?php
 
 /**
- * Class for localization updates.
- *
- * @todo Refactor code to remove duplication
+ * Class for localization update hooks and static methods.
  */
 class LocalisationUpdate {
-
-       private static $newHashes = null;
-       private static $filecache = array();
-
        /**
-        * LocalisationCacheRecache hook handler.
-        *
-        * @param $lc LocalisationCache
-        * @param $langcode String
-        * @param $cache Array
-        *
-        * @return true
+        * Hook: LocalisationCacheRecache
         */
-       public static function onRecache( LocalisationCache $lc, $langcode, 
array &$cache ) {
-               // Handle fallback sequence and load all fallback messages from 
the cache
-               $codeSequence = array_merge( array( $langcode ), 
$cache['fallbackSequence'] );
-               // Iterate over the fallback sequence in reverse, otherwise the 
fallback
-               // language will override the requested language
-               foreach ( array_reverse( $codeSequence ) as $code ) {
-                       if ( $code == 'en' ) {
-                               // Skip English, otherwise we end up trying to 
read
-                               // the nonexistent cache file for en a couple 
hundred times
-                               continue;
+       public static function onRecache( LocalisationCache $lc, $code, array 
&$cache ) {
+               $dir = LocalisationUpdate::getDirectory();
+               if ( !$dir ) {
+                       return true;
+               }
+
+               $codeSequence = array_merge( array( $code ), 
$cachedData['fallbackSequence'] );
+               foreach ( $codeSequence as $csCode ) {
+                       $fileName = "$dir/" . self::getFilename( $csCode );
+                       if ( is_readable( $fileName ) ) {
+                               $data = FormatJson::decode( file_get_contents( 
$fileName ), true );
+                               $cachedData['messages'] = array_merge( $data, 
$cachedData['messages'] );
                        }
 
-                       $cache['messages'] = array_merge(
-                               $cache['messages'],
-                               self::readFile( $code )
-                       );
-
-                       $cache['deps'][] = new FileDependency(
-                               self::filename( $code )
-                       );
+                       $cachedData['deps'][] = new FileDependency( $fileName );
                }
 
                return true;
        }
 
        /**
-        * Called from the cronjob to fetch new messages from SVN.
+        * Returns a directory where updated translations are stored.
         *
-        * @param $options Array
-        *
-        * @return true
+        * @return string|false False if not configured.
+        * @since 1.1
         */
-       public static function updateMessages( array $options ) {
-               global $wgLocalisationUpdateDirectory, 
$wgLocalisationUpdateCoreURL,
-                       $wgLocalisationUpdateExtensionURL, 
$wgLocalisationUpdateSVNURL;
-
-               $verbose = !isset( $options['quiet'] );
-               $all = isset( $options['all'] );
-               $skipCore = isset( $options['skip-core'] );
-               $skipExtensions = isset( $options['skip-extensions'] );
-
-               if ( isset( $options['outdir'] ) ) {
-                       $wgLocalisationUpdateDirectory = $options['outdir'];
-               }
-
-               $coreUrl = $wgLocalisationUpdateCoreURL;
-               $extUrl = $wgLocalisationUpdateExtensionURL;
-
-               // Some ugly BC
-               if ( $wgLocalisationUpdateSVNURL ) {
-                       $coreUrl = $wgLocalisationUpdateSVNURL . '/phase3/$2';
-                       $extUrl = $wgLocalisationUpdateSVNURL . 
'/extensions/$1/$2';
-               }
-
-               // Some more ugly BC
-               if ( isset( $options['svnurl'] ) ) {
-                       $coreUrl = $options['svnurl'] . '/phase3/$2';
-                       $extUrl = $options['svnurl'] . '/extensions/$1/$2';
-               }
-
-               $result = 0;
-
-               // Update all MW core messages.
-               if ( !$skipCore ) {
-                       $result = self::updateMediawikiMessages( $verbose, 
$coreUrl );
-               }
-
-               // Update all Extension messages.
-               if ( !$skipExtensions ) {
-                       if ( $all ) {
-                               global $IP;
-                               $extFiles = array();
-
-                               // Look in extensions/ for all available 
items...
-                               // @todo Add support for $wgExtensionAssetsPath
-                               $dirs = new RecursiveDirectoryIterator( 
"$IP/extensions/" );
-
-                               // I ain't kidding... RecursiveIteratorIterator.
-                               foreach ( new RecursiveIteratorIterator( $dirs 
) as $pathname => $item ) {
-                                       $filename = basename( $pathname );
-                                       $matches = array();
-                                       if ( preg_match( '/^(.*)\.i18n\.php$/', 
$filename, $matches ) ) {
-                                               $group = $matches[1];
-                                               $extFiles[$group] = $pathname;
-                                       }
-                               }
-                       } else {
-                               global $wgExtensionMessagesFiles;
-                               $extFiles = $wgExtensionMessagesFiles;
-                       }
-                       foreach ( $extFiles as $extension => $locFile ) {
-                               $result += self::updateExtensionMessages( 
$locFile, $extension, $verbose, $extUrl );
-                       }
-               }
-
-               self::writeHashes();
-
-               // And output the result!
-               self::myLog( "Updated {$result} messages in total" );
-               self::myLog( "Done" );
-
-               return true;
-       }
-
-       /**
-        * Update Extension Messages.
-        *
-        * @param $file String
-        * @param $extension String
-        * @param $verbose Boolean
-        *
-        * @return Integer: the amount of updated messages
-        */
-       public static function updateExtensionMessages( $file, $extension, 
$verbose, $extUrl ) {
-               $match = array();
-               $ok = preg_match( '~^.*/extensions/([^/]+)/(.*)$~U', $file, 
$match );
-               if ( !$ok ) {
-                       return null;
-               }
-
-               $ext = $match[1];
-               $extFile = $match[2];
-
-               // Create a full path.
-               $svnfile = str_replace(
-                       array( '$1', '$2', '$3', '$4' ),
-                       array( $ext, $extFile, urlencode( $ext ), urlencode( 
$extFile ) ),
-                       $extUrl
-               );
-
-               // Compare the 2 files.
-               $result = self::compareExtensionFiles( $extension, $svnfile, 
$file, $verbose );
-
-               return $result;
-       }
-
-       /**
-        * Update the MediaWiki Core Messages.
-        *
-        * @param $verbose Boolean
-        *
-        * @return Integer: the amount of updated messages
-        */
-       public static function updateMediawikiMessages( $verbose, $coreUrl ) {
-               // Find the changed English strings (as these messages won't be 
updated in ANY language).
-               $localUrl = Language::getMessagesFileName( 'en' );
-               $repoUrl = str_replace(
-                       array( '$2', '$4' ),
-                       array( 'languages/messages/MessagesEn.php', 
'languages%2Fmessages%2FMessagesEn.php' ),
-                       $coreUrl
-               );
-               $changedEnglishStrings = self::compareFiles( $repoUrl, 
$localUrl, $verbose );
-
-               // Count the changes.
-               $changedCount = 0;
-
-               $languages = Language::fetchLanguageNames( null, 'mwfile' );
-               foreach ( array_keys( $languages ) as $code ) {
-                       $localUrl = Language::getMessagesFileName( $code );
-                       // Not prefixed with $IP
-                       $filename = Language::getFilename( 
'languages/messages/Messages', $code );
-                       $repoUrl = str_replace(
-                               array( '$2', '$4' ),
-                               array( $filename, urlencode( $filename ) ),
-                               $coreUrl
-                       );
-
-                       // Compare the files.
-                       $changedCount += self::compareFiles(
-                               $repoUrl,
-                               $localUrl,
-                               $verbose,
-                               $changedEnglishStrings,
-                               false,
-                               true
-                       );
-               }
-
-               // Log some nice info.
-               self::myLog( "{$changedCount} MediaWiki messages are updated" );
-
-               return $changedCount;
-       }
-
-       /**
-        * Removes all unneeded content from a file and returns it.
-        *
-        * @param $contents String
-        *
-        * @return String
-        */
-       public static function cleanupFile( $contents ) {
-               // We don't need any PHP tags.
-               $contents = strtr( $contents,
-                       array(
-                               '<?php' => '',
-                               '?' . '>' => ''
-                       )
-               );
-
-               $results = array();
-
-               // And we only want message arrays.
-               preg_match_all( '/\$messages(.*\s)*?\);/', $contents, $results 
);
-
-               // But we want them all in one string.
-               if ( !empty( $results[0] ) && is_array( $results[0] ) ) {
-                       $contents = implode( "\n\n", $results[0] );
-               } else {
-                       $contents = '';
-               }
-
-               // And we hate the windows vs linux linebreaks.
-               $contents = preg_replace( '/\r\n?/', "\n", $contents );
-
-               return $contents;
-       }
-
-       /**
-        * Returns the contents of a file or false on failiure.
-        *
-        * @param $file String
-        *
-        * @return string or false
-        */
-       public static function getFileContents( $file ) {
-               global $wgLocalisationUpdateRetryAttempts;
-
-               $attempts = 0;
-               $filecontents = '';
-
-               // Use cURL to get the SVN contents.
-               if ( preg_match( "/^http/", $file ) ) {
-                       while ( !$filecontents && $attempts <= 
$wgLocalisationUpdateRetryAttempts ) {
-                               if ( $attempts > 0 ) {
-                                       $delay = 1;
-                                       self::myLog( 'Failed to download ' . 
$file . "; retrying in ${delay}s..." );
-                                       sleep( $delay );
-                               }
-
-                               $filecontents = Http::get( $file );
-                               $attempts++;
-                       }
-                       if ( !$filecontents ) {
-                               self::myLog( 'Cannot get the contents of ' . 
$file . ' (curl)' );
-
-                               return false;
-                       }
-               } else { // otherwise try file_get_contents
-                       if ( !( $filecontents = file_get_contents( $file ) ) ) {
-                               self::myLog( 'Cannot get the contents of ' . 
$file );
-
-                               return false;
-                       }
-               }
-
-               return $filecontents;
-       }
-
-       /**
-        * Returns a pair of arrays containing the messages from two files, or
-        * a pair of nulls if the files don't need to be checked.
-        *
-        * @param $tag String
-        * @param $file1 String
-        * @param $file2 String
-        * @param $verbose Boolean
-        * @param $alwaysGetResult Boolean
-        *
-        * @return array
-        */
-       public static function loadFilesToCompare( $tag, $file1, $file2, 
$verbose,
-               $alwaysGetResult = true
-       ) {
-               $file1contents = self::getFileContents( $file1 );
-               if ( $file1contents === false || $file1contents === '' ) {
-                       self::myLog( "Failed to read $file1" );
-
-                       return array( null, null );
-               }
-
-               $file2contents = self::getFileContents( $file2 );
-               if ( $file2contents === false || $file2contents === '' ) {
-                       self::myLog( "Failed to read $file2" );
-
-                       return array( null, null );
-               }
-
-               // Only get the part we need.
-               $file1contents = self::cleanupFile( $file1contents );
-               $file1hash = md5( $file1contents );
-
-               $file2contents = self::cleanupFile( $file2contents );
-               $file2hash = md5( $file2contents );
-
-               // Check if the file has changed since our last update.
-               if ( !$alwaysGetResult ) {
-                       if ( !self::checkHash( $file1, $file1hash ) && 
!self::checkHash( $file2, $file2hash ) ) {
-                               self::myLog(
-                                       "Skipping {$tag} since the files 
haven't changed since our last update",
-                                       $verbose
-                               );
-
-                               return array( null, null );
-                       }
-               }
-
-               // Get the array with messages.
-               $messages1 = self::parsePHP( $file1contents, 'messages' );
-               if ( !is_array( $messages1 ) ) {
-                       if ( strpos( $file1contents, '$messages' ) === false ) {
-                               // No $messages array. This happens for some 
languages that only have a fallback
-                               $messages1 = array();
-                       } else {
-                               // Broken file? Report and bail
-                               self::myLog( "Failed to parse $file1" );
-
-                               return array( null, null );
-                       }
-               }
-
-               $messages2 = self::parsePHP( $file2contents, 'messages' );
-               if ( !is_array( $messages2 ) ) {
-                       // Broken file? Report and bail
-                       if ( strpos( $file2contents, '$messages' ) === false ) {
-                               // No $messages array. This happens for some 
languages that only have a fallback
-                               $messages2 = array();
-                       } else {
-                               self::myLog( "Failed to parse $file2" );
-
-                               return array( null, null );
-                       }
-               }
-
-               self::saveHash( $file1, $file1hash );
-               self::saveHash( $file2, $file2hash );
-
-               return array( $messages1, $messages2 );
-       }
-
-       /**
-        * Compare new and old messages lists, and optionally save the new
-        * messages if they've changed.
-        *
-        * @param $langcode String
-        * @param $old_messages Array
-        * @param $new_messages Array
-        * @param $verbose Boolean
-        * @param $forbiddenKeys Array
-        * @param $saveResults Boolean
-        *
-        * @return array|int
-        */
-       private static function compareLanguageArrays( $langcode, $old_messages,
-               $new_messages, $verbose, $forbiddenKeys, $saveResults
-       ) {
-               // Get the currently-cached messages, if any
-               $cur_messages = self::readFile( $langcode );
-
-               // Update the messages lists with the cached messages
-               $old_messages = array_merge( $old_messages, $cur_messages );
-               $new_messages = array_merge( $cur_messages, $new_messages );
-
-               // Use the old/cached version for any forbidden keys
-               if ( count( $forbiddenKeys ) ) {
-                       $new_messages = array_merge(
-                               array_diff_key( $new_messages, $forbiddenKeys ),
-                               array_intersect_key( $old_messages, 
$forbiddenKeys )
-                       );
-               }
-
-               if ( $saveResults ) {
-                       // If anything has changed from the saved version, save 
the new version
-                       if ( $new_messages != $cur_messages ) {
-                               // Count added, updated, and deleted messages:
-                               // diff( new, cur ) gives added + updated, and 
diff( cur, new )
-                               // gives deleted + updated.
-                               $changed = array_diff_assoc( $new_messages, 
$cur_messages ) +
-                                       array_diff_assoc( $cur_messages, 
$new_messages );
-                               $updates = count( $changed );
-                               self::myLog( "{$updates} messages updated for 
{$langcode}.", $verbose );
-                               self::writeFile( $langcode, $new_messages );
-                       } else {
-                               $updates = 0;
-                       }
-
-                       return $updates;
-               } else {
-                       // Find all deleted or changed messages
-                       $changedStrings = array_diff_assoc( $old_messages, 
$new_messages );
-
-                       return $changedStrings;
-               }
-       }
-
-       /**
-        * Returns an array containing the differences between the files.
-        *
-        * @param $newfile String
-        * @param $oldfile String
-        * @param $verbose Boolean
-        * @param $forbiddenKeys Array
-        * @param $alwaysGetResult Boolean
-        * @param $saveResults Boolean
-        *
-        * @return array|int
-        */
-       public static function compareFiles( $newfile, $oldfile, $verbose,
-               array $forbiddenKeys = array(), $alwaysGetResult = true, 
$saveResults = false
-       ) {
-               // Get the languagecode.
-               $langcode = Language::getCodeFromFileName( $newfile, 'Messages' 
);
-
-               list( $new_messages, $old_messages ) = self::loadFilesToCompare(
-                       $langcode, $newfile, $oldfile, $verbose, 
$alwaysGetResult
-               );
-               if ( $new_messages === null || $old_messages === null ) {
-                       return $saveResults ? 0 : array();
-               }
-
-               return self::compareLanguageArrays(
-                       $langcode,
-                       $old_messages,
-                       $new_messages,
-                       $verbose,
-                       $forbiddenKeys,
-                       $saveResults
-               );
-       }
-
-       /**
-        *
-        * @param $extension String
-        * @param $newfile String
-        * @param $oldfile String
-        * @param $verbose Boolean
-        * @param $alwaysGetResult Boolean
-        * @param $saveResults Boolean
-        *
-        * @return Integer: the amount of updated messages
-        */
-       public static function compareExtensionFiles( $extension, $newfile, 
$oldfile, $verbose ) {
-               list( $new_messages, $old_messages ) = self::loadFilesToCompare(
-                       $extension, $newfile, $oldfile, $verbose, false
-               );
-               if ( $new_messages === null || $old_messages === null ) {
-                       return 0;
-               }
-
-               // Update counter.
-               $updates = 0;
-
-               if ( empty( $new_messages['en'] ) ) {
-                       $new_messages['en'] = array();
-               }
-
-               if ( empty( $old_messages['en'] ) ) {
-                       $old_messages['en'] = array();
-               }
-
-               // Find the changed english strings.
-               $forbiddenKeys = self::compareLanguageArrays(
-                       'en',
-                       $old_messages['en'],
-                       $new_messages['en'],
-                       $verbose,
-                       array(),
-                       false
-               );
-
-               // Do an update for each language.
-               foreach ( $new_messages as $language => $messages ) {
-                       if ( $language == 'en' ) { // Skip english.
-                               continue;
-                       }
-
-                       if ( !isset( $old_messages[$language] ) ) {
-                               $old_messages[$language] = array();
-                       }
-
-                       $updates += self::compareLanguageArrays(
-                               $language,
-                               $old_messages[$language],
-                               $messages,
-                               $verbose,
-                               $forbiddenKeys,
-                               true
-                       );
-               }
-
-               // And log some stuff.
-               self::myLog( "Updated " . $updates . " messages for the 
'{$extension}' extension", $verbose );
-
-               return $updates;
-       }
-
-       /**
-        * Checks whether a messages file has a certain hash.
-        *
-        * @todo Swap return values, this is insane
-        *
-        * @param $file string Filename
-        * @param $hash string Hash
-        *
-        * @return bool True if $file does NOT have hash $hash, false if it does
-        */
-       public static function checkHash( $file, $hash ) {
-               $hashes = self::readFile( 'hashes' );
-
-               wfSuppressWarnings();
-               $return = $hashes[$file] !== $hash;
-               wfRestoreWarnings();
-
-               return $return;
-       }
-
-       /**
-        * @param $file
-        * @param $hash
-        */
-       public static function saveHash( $file, $hash ) {
-               if ( is_null( self::$newHashes ) ) {
-                       self::$newHashes = self::readFile( 'hashes' );
-               }
-
-               self::$newHashes[$file] = $hash;
-       }
-
-       public static function writeHashes() {
-               self::writeFile( 'hashes', self::$newHashes );
-       }
-
-       /**
-        * Logs a message.
-        *
-        * @param $log String
-        * @param bool $verbose
-        */
-       public static function myLog( $log, $verbose = true ) {
-               if ( !$verbose ) {
-                       return;
-               }
-               if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', 
$_SERVER ) ) {
-                       wfDebug( $log . "\n" );
-               } else {
-                       print "$log\n";
-               }
-       }
-
-       /**
-        * @param $php
-        * @param $varname
-        * @return bool|array
-        */
-       public static function parsePHP( $php, $varname ) {
-               try {
-                       $reader = new QuickArrayReader( "<?php $php" );
-
-                       return $reader->getVar( $varname );
-               } catch ( Exception $e ) {
-                       self::myLog( "Failed to read file: " . $e );
-
-                       return false;
-               }
-       }
-
-       /**
-        * @param $lang
-        * @return string
-        * @throws MWException
-        */
-       public static function filename( $lang ) {
+       public static function getDirectory() {
                global $wgLocalisationUpdateDirectory, $wgCacheDirectory;
 
-               $dir = $wgLocalisationUpdateDirectory ?
+               // ?: can be used once we drop support for MW 1.19
+               return $wgLocalisationUpdateDirectory ?
                        $wgLocalisationUpdateDirectory :
                        $wgCacheDirectory;
-
-               if ( !$dir ) {
-                       throw new MWException( 'No cache directory configured' 
);
-               }
-
-               return "$dir/l10nupdate-$lang.cache";
        }
 
        /**
-        * @param $lang
-        * @return mixed
+        * Returns a filename where updated translations are stored.
+        *
+        * @param string $language Language tag
+        * @return string
+        * @since 1.1
         */
-       public static function readFile( $lang ) {
-               if ( !isset( self::$filecache[$lang] ) ) {
-                       $file = self::filename( $lang );
-                       wfSuppressWarnings();
-                       $contents = file_get_contents( $file );
-                       wfRestoreWarnings();
-
-                       if ( $contents === false ) {
-                               wfDebug( "Failed to read file '$file'\n" );
-                               $retval = array();
-                       } else {
-                               $retval = unserialize( $contents );
-
-                               if ( $retval === false ) {
-                                       wfDebug( "Corrupted data in file 
'$file'\n" );
-                                       $retval = array();
-                               }
-                       }
-                       self::$filecache[$lang] = $retval;
-               }
-
-               return self::$filecache[$lang];
-       }
-
-       /**
-        * @param $lang
-        * @param $var
-        * @throws MWException
-        */
-       public static function writeFile( $lang, $var ) {
-               $file = self::filename( $lang );
-
-               wfSuppressWarnings();
-               if ( !file_put_contents( $file, serialize( $var ) ) ) {
-                       throw new MWException( "Failed to write to file 
'$file'" );
-               }
-               wfRestoreWarnings();
-
-               self::$filecache[$lang] = $var;
+       public static function getFilename( $language ) {
+               return "l10nupdate-$language.json";
        }
 }
diff --git a/LocalisationUpdate.php b/LocalisationUpdate.php
index eac97e9..46f116b 100644
--- a/LocalisationUpdate.php
+++ b/LocalisationUpdate.php
@@ -12,33 +12,43 @@
 $wgLocalisationUpdateDirectory = false;
 
 /**
- * These should point to either an HTTP-accessible file or local file system.
- * $1 is the name of the repo (for extensions) and $2 is the name of file in 
the repo.
- * $3 and $4 are the same, respectively, but urlencoded for e.g. gitblit.
+ * Default repository source to use.
+ * @since 2013-03
  */
+$wgLocalisationUpdateRepository = 'github';
 
-$wgLocalisationUpdateCoreURL = 
"https://git.wikimedia.org/raw/mediawiki%2Fcore.git/HEAD/$4";;
-$wgLocalisationUpdateExtensionURL =
-       "https://git.wikimedia.org/raw/mediawiki%2Fextensions%2F$3.git/HEAD/$4";;
+/**
+ * Available repository sources.
+ * @since 2013-03
+ */
+$wgLocalisationUpdateRepositories = array();
+$wgLocalisationUpdateRepositories['github'] = array(
+       'mediawiki' =>
+               'https://raw.github.com/wikimedia/mediawiki-core/master/%PATH%',
+       'extension' =>
+               
'https://raw.github.com/wikimedia/mediawiki-extensions-%NAME%/master/%PATH%',
+);
 
-/// Deprecated
-$wgLocalisationUpdateSVNURL = false;
+// Example for local filesystem configuration
+$wgLocalisationUpdateRepositories['local'] = array(
+       'mediawiki' =>
+               'file:///resources/projects/mediawiki/master/%PATH%',
+       'extension' =>
+               
'file:///resources/projects/mediawiki-extensions/extensions/%NAME%/%PATH%',
+);
 
-$wgLocalisationUpdateRetryAttempts = 5;
-
-// Info about me!
 $wgExtensionCredits['other'][] = array(
        'path' => __FILE__,
        'name' => 'LocalisationUpdate',
        'author' => array( 'Tom Maaswinkel', 'Niklas Laxström', 'Roan Kattouw' 
),
-       'version' => '1.0',
+       'version' => '1.1',
        'url' => 'https://www.mediawiki.org/wiki/Extension:LocalisationUpdate',
        'descriptionmsg' => 'localisationupdate-desc',
 );
 
 $wgHooks['LocalisationCacheRecache'][] = 'LocalisationUpdate::onRecache';
 
-$dir = __DIR__ . '/';
-$wgExtensionMessagesFiles['LocalisationUpdate'] = $dir . 
'LocalisationUpdate.i18n.php';
+$dir = __DIR__;
+$wgExtensionMessagesFiles['LocalisationUpdate'] = 
"$dir/LocalisationUpdate.i18n.php";
 
 require "$dir/Autoload.php";
diff --git a/update.php b/update.php
index 04ea64c..ac40308 100644
--- a/update.php
+++ b/update.php
@@ -3,35 +3,73 @@
 $IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
        ? getenv( 'MW_INSTALL_PATH' )
        : realpath( dirname( __FILE__ ) . "/../../" );
+// Can use __DIR__ once we drop support for MW 1.19
 
-// TODO: migrate to maintenance class
-require_once "$IP/maintenance/commandLine.inc";
+require "$IP/maintenance/Maintenance.php";
 
-if ( isset( $options['help'] ) ) {
-       print "Fetches updated localisation files from MediaWiki development 
SVN\n";
-       print "and saves into local database to merge with release defaults.\n";
-       print "\n";
-       print "Usage: php extensions/LocalisationUpdate/update.php\n";
-       print "Options:\n";
-       print "  --quiet           Suppress progress output\n";
-       print "  --skip-core       Don't fetch MediaWiki core files\n";
-       print "  --skip-extensions Don't fetch any extension files\n";
-       print "  --all             Fetch all present extensions, not just those 
enabled\n";
-       print "  --outdir=<dir>    Override output directory for serialized 
update files\n";
-       print "  --svnurl=<url>    URL to SVN repository, or path to local SVN 
checkout. Deprecated.\n";
-       print "\n";
-       exit( 0 );
+class LU extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = 'Fetches translation updates to MediaWiki 
and extensions.';
+               $this->addOption(
+                       'repoid',
+                       'Fetch translations from repositores identified by 
this',
+                       false, /*required*/
+                       true /*has arg*/
+               );
+       }
+
+       public function execute() {
+               // Prevent the script from timing out
+               set_time_limit( 0 );
+               ini_set( "max_execution_time", 0 );
+               ini_set( 'memory_limit', -1 );
+
+               global $wgExtensionMessagesFiles, $wgMessagesDirs, $IP;
+               global $wgLocalisationUpdateRepositories;
+               global $wgLocalisationUpdateRepository;
+
+               $dir = LocalisationUpdate::getDirectory();
+               if ( !$dir ) {
+                       $this->error( "No cache directory configured", true );
+                       return;
+               }
+
+               $finder = new LU_Finder( $wgExtensionMessagesFiles, 
$wgMessagesDirs, $IP );
+               $readerFactory = new LU_ReaderFactory();
+               $fetcherFactory = new LU_FetcherFactory();
+
+               $repoid = $this->getOption( 'repoid', 
$wgLocalisationUpdateRepository );
+               if ( !isset( $wgLocalisationUpdateRepositories[$repoid] ) ) {
+                       $known = implode( ', ', array_keys( 
$wgLocalisationUpdateRepositories ) );
+                       $this->error( "Unknown repoid $repoid; known: $known", 
true );
+                       return;
+               }
+               $repos = $wgLocalisationUpdateRepositories[$repoid];
+
+               // Do it ;)
+               $updater = new LU_Updater();
+               $updatedMessages = $updater->execute(
+                       $finder,
+                       $readerFactory,
+                       $fetcherFactory,
+                       $repos
+               );
+
+               // Store it ;)
+               $count = array_sum( array_map( 'count', $updatedMessages ) );
+               if ( !$count ) {
+                       $this->output( "Found no new translations\n" );
+                       return;
+               }
+
+               foreach ( $updatedMessages as $language => $messages ) {
+                       $filename = "$dir/" . LocalisationUpdate::getFilename( 
$language );
+                       file_put_contents( $filename, FormatJson::encode( 
$messages, true ) );
+               }
+               $this->output( "Saved $count new translations\n" );
+       }
 }
 
-$starttime = microtime( true );
-
-// Prevent the script from timing out
-set_time_limit( 0 );
-ini_set( "max_execution_time", 0 );
-ini_set( 'memory_limit', -1 );
-
-LocalisationUpdate::updateMessages( $options );
-
-$endtime = microtime( true );
-$totaltime = ( $endtime - $starttime );
-print "All done in " . $totaltime . " seconds\n";
+$maintClass = 'LU';
+require_once RUN_MAINTENANCE_IF_MAIN;

-- 
To view, visit https://gerrit.wikimedia.org/r/118446
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I21ce94ae2262d9c90132d21c4c9f8ebdd08a14ea
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/LocalisationUpdate
Gerrit-Branch: json-rewrite
Gerrit-Owner: Nikerabbit <niklas.laxst...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to