Merge "Add Turoyo (tru) language to MediaWiki"
authorSiebrand <siebrand@wikimedia.org>
Thu, 26 Jul 2012 18:45:49 +0000 (18:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 26 Jul 2012 18:45:49 +0000 (18:45 +0000)
73 files changed:
RELEASE-NOTES-1.20
includes/Article.php
includes/AutoLoader.php
includes/DefaultSettings.php
includes/Defines.php
includes/Linker.php
includes/Setup.php
includes/Wiki.php
includes/ZhConversion.php
includes/api/ApiQuery.php
includes/api/ApiQueryDuplicateFiles.php
includes/api/ApiQueryFilearchive.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryLogEvents.php
includes/db/IORMRow.php
includes/db/IORMTable.php
includes/db/ORMResult.php
includes/db/ORMRow.php
includes/db/ORMTable.php
includes/filerepo/FileRepo.php
includes/filerepo/LocalRepo.php
includes/filerepo/RepoGroup.php
includes/filerepo/backend/SwiftFileBackend.php
includes/media/Generic.php [deleted file]
includes/media/ImageHandler.php [new file with mode: 0644]
includes/media/MediaHandler.php [new file with mode: 0644]
includes/objectcache/MemcachedClient.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/specials/SpecialBlock.php
includes/specials/SpecialVersion.php
includes/zhtable/toCN.manual
includes/zhtable/tradphrases.manual
languages/Language.php
languages/LanguageConverter.php
languages/messages/MessagesEn.php
languages/messages/MessagesKk_cn.php
languages/messages/MessagesKu_arab.php
maintenance/backupTextPass.inc
maintenance/deleteBatch.php
maintenance/edit.php
maintenance/fixSlaveDesync.php
maintenance/generateSitemap.php
maintenance/getLagTimes.php
maintenance/getSlaveServer.php
maintenance/getText.php
maintenance/importDump.php
maintenance/importImages.inc
maintenance/importImages.php
maintenance/importSiteScripts.php
maintenance/importTextFile.php
maintenance/initStats.php
maintenance/install.php
maintenance/jsparse.php
maintenance/lag.php
maintenance/mctest.php
maintenance/mergeMessageFileList.php
maintenance/migrateUserGroup.php
maintenance/minify.php
maintenance/moveBatch.php
maintenance/namespaceDupes.php
maintenance/nextJobDB.php
maintenance/nukeNS.php
maintenance/nukePage.php
maintenance/orphans.php
maintenance/syncFileBackend.php
resources/Resources.php
resources/mediawiki.action/mediawiki.action.edit.js
resources/mediawiki/mediawiki.Uri.js
resources/mediawiki/mediawiki.js
skins/common/commonElements.css
skins/common/shared.css
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js

index fecb0d9..2521804 100644 (file)
@@ -99,6 +99,8 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   instead of the site content language
 * (bug 37926) Deleterevision will no longer allow users to delete log entries,
   the new deletelogentry permission is required for this.
+* (bug 14237) Allow PAGESINCATEGORY to distinguish between 'all', 'pages', 'files'
+  and 'subcats'
 
 === Bug fixes in 1.20 ===
 * (bug 30245) Use the correct way to construct a log page title.
@@ -150,7 +152,6 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   who don't have access to /tmp can specify an alternative.
 * (bug 27283) SqlBagOStuff breaks PostgreSQL transactions.
 * (bug 35727) mw.Api ajax() should put token parameter last.
-* (bug 260) Handle <pre> overflow automatically with a scroll bar.
 * (bug 37708) mw.Uri.clone() should make a deep copy.
 * (bug 38024) ResourceLoader should not create empty stylesheets for modules
   that don't have stylesheets.
@@ -169,6 +170,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   values are used instead of just the fixed values from when the tablesorter
   was initialized.
 * (bug 38093) Gender of changed user groups missing in Special:Log/rights
+* (bug 35893) Special:Block needs to load mediawiki.special.block.js.
 
 === API changes in 1.20 ===
 * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
@@ -198,6 +200,8 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * (bug 36987) API avoids mangling fields in continuation parameters
 * (bug 30836) siteinfo prop=specialpagealiases will no longer return nonexistent special pages
 * (bug 38190) Add "required" flag to some token params for hint in api docs.
+* (bug 27567) Add file repo support to prop=duplicatefiles.
+* (bug 27610) Add archivename for non-latest image version to list=filearchive
 
 === Languages updated in 1.20 ===
 
index 3b259e2..18ca077 100644 (file)
@@ -1075,8 +1075,9 @@ class Article extends Page {
                }
 
                $outputPage = $this->getContext()->getOutput();
+               $user = $this->getContext()->getUser();
                // If the user is not allowed to see it...
-               if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
+               if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
                        $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                'rev-deleted-text-permission' );
 
index 6c37d1a..2987b31 100644 (file)
@@ -689,11 +689,11 @@ $wgAutoloadLocalClasses = array(
        'FormatMetadata' => 'includes/media/FormatMetadata.php',
        'GIFHandler' => 'includes/media/GIF.php',
        'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php',
-       'ImageHandler' => 'includes/media/Generic.php',
+       'ImageHandler' => 'includes/media/ImageHandler.php',
        'IPTC' => 'includes/media/IPTC.php',
        'JpegHandler' => 'includes/media/Jpeg.php',
        'JpegMetadataExtractor' => 'includes/media/JpegMetadataExtractor.php',
-       'MediaHandler' => 'includes/media/Generic.php',
+       'MediaHandler' => 'includes/media/MediaHandler.php',
        'MediaTransformError' => 'includes/media/MediaTransformOutput.php',
        'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php',
        'PNGHandler' => 'includes/media/PNG.php',
index 3311fb1..d7feab8 100644 (file)
@@ -1584,17 +1584,10 @@ $wgUseDumbLinkUpdate = false;
 
 /**
  * Anti-lock flags - bitfield
- *   - ALF_PRELOAD_LINKS:
- *       Preload links during link update for save
- *   - ALF_PRELOAD_EXISTENCE:
- *       Preload cur_id during replaceLinkHolders
  *   - ALF_NO_LINK_LOCK:
  *       Don't use locking reads when updating the link table. This is
  *       necessary for wikis with a high edit rate for performance
  *       reasons, but may cause link table inconsistency
- *   - ALF_NO_BLOCK_LOCK:
- *       As for ALF_LINK_LOCK, this flag is a necessity for high-traffic
- *       wikis.
  */
 $wgAntiLockFlags = 0;
 
@@ -5510,10 +5503,12 @@ $wgLogActions = array(
  * @see LogFormatter
  */
 $wgLogActionsHandlers = array(
-       // move, move_redir
-       'move/*'            => 'MoveLogFormatter',
-       // delete, restore, revision, event
-       'delete/*'          => 'DeleteLogFormatter',
+       'move/move'         => 'MoveLogFormatter',
+       'move/move_redir'  => 'MoveLogFormatter',
+       'delete/delete'     => 'DeleteLogFormatter',
+       'delete/restore'    => 'DeleteLogFormatter',
+       'delete/revision'   => 'DeleteLogFormatter',
+       'delete/event'      => 'DeleteLogFormatter',
        'suppress/revision' => 'DeleteLogFormatter',
        'suppress/event'    => 'DeleteLogFormatter',
        'suppress/delete'   => 'DeleteLogFormatter',
index f7b41b8..56218d6 100644 (file)
@@ -144,8 +144,8 @@ define( 'AV_SCAN_FAILED', false );  #scan failed (scanner not found or error in
  * Anti-lock flags
  * See DefaultSettings.php for a description
  */
-define( 'ALF_PRELOAD_LINKS', 1 );
-define( 'ALF_PRELOAD_EXISTENCE', 2 );
+define( 'ALF_PRELOAD_LINKS', 1 ); // unused
+define( 'ALF_PRELOAD_EXISTENCE', 2 ); // unused
 define( 'ALF_NO_LINK_LOCK', 4 );
 define( 'ALF_NO_BLOCK_LOCK', 8 );
 /**@}*/
index 5355140..083845d 100644 (file)
@@ -139,9 +139,9 @@ class Linker {
                if ( $t->isRedirect() ) {
                        # Page is a redirect
                        $colour = 'mw-redirect';
-               } elseif ( $threshold > 0 &&
-                          $t->exists() && $t->getLength() < $threshold &&
-                          $t->isContentPage() ) {
+               } elseif ( $threshold > 0 && $t->isContentPage() &&
+                       $t->exists() && $t->getLength() < $threshold
+               ) {
                        # Page is a stub
                        $colour = 'stub';
                }
index 18a880e..dedfca9 100644 (file)
@@ -365,8 +365,10 @@ if ( $wgNewUserLog ) {
        $wgLogTypes[]                        = 'newusers';
        $wgLogNames['newusers']              = 'newuserlogpage';
        $wgLogHeaders['newusers']            = 'newuserlogpagetext';
-       # newusers, create, create2, autocreate
-       $wgLogActionsHandlers['newusers/*']  = 'NewUsersLogFormatter';
+       $wgLogActionsHandlers['newusers/newusers'] = 'NewUsersLogFormatter';
+       $wgLogActionsHandlers['newusers/create'] = 'NewUsersLogFormatter';
+       $wgLogActionsHandlers['newusers/create2'] = 'NewUsersLogFormatter';
+       $wgLogActionsHandlers['newusers/autocreate'] = 'NewUsersLogFormatter';
 }
 
 if ( $wgCookieSecure === 'detect' ) {
index 5f593e7..4b84744 100644 (file)
@@ -133,6 +133,34 @@ class MediaWiki {
                return $this->context->getTitle();
        }
 
+       /**
+        * Returns the name of the action that will be executed.
+        *
+        * @return string: action
+        */
+       public function getAction() {
+               static $action = null;
+
+               if ( $action === null ) {
+                       $action = Action::getActionName( $this->context );
+               }
+
+               return $action;
+       }
+
+       /**
+        * Create an Article object of the appropriate class for the given page.
+        *
+        * @deprecated in 1.18; use Article::newFromTitle() instead
+        * @param $title Title
+        * @param $context IContextSource
+        * @return Article object
+        */
+       public static function articleFromTitle( $title, IContextSource $context ) {
+               wfDeprecated( __METHOD__, '1.18' );
+               return Article::newFromTitle( $title, $context );
+       }
+
        /**
         * Performs the request.
         * - bad titles
@@ -291,34 +319,6 @@ class MediaWiki {
                wfProfileOut( __METHOD__ );
        }
 
-       /**
-        * Create an Article object of the appropriate class for the given page.
-        *
-        * @deprecated in 1.18; use Article::newFromTitle() instead
-        * @param $title Title
-        * @param $context IContextSource
-        * @return Article object
-        */
-       public static function articleFromTitle( $title, IContextSource $context ) {
-               wfDeprecated( __METHOD__, '1.18' );
-               return Article::newFromTitle( $title, $context );
-       }
-
-       /**
-        * Returns the name of the action that will be executed.
-        *
-        * @return string: action
-        */
-       public function getAction() {
-               static $action = null;
-               
-               if ( $action === null ) {
-                       $action = Action::getActionName( $this->context );
-               }
-
-               return $action;
-       }
-
        /**
         * Initialize the main Article object for "standard" actions (view, etc)
         * Create an Article object for the page, following redirects if needed.
@@ -392,64 +392,6 @@ class MediaWiki {
                return $article;
        }
 
-       /**
-        * Do a job from the job queue
-        */
-       private function doJobs() {
-               global $wgJobRunRate;
-
-               if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
-                       return;
-               }
-               if ( $wgJobRunRate < 1 ) {
-                       $max = mt_getrandmax();
-                       if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
-                               return;
-                       }
-                       $n = 1;
-               } else {
-                       $n = intval( $wgJobRunRate );
-               }
-
-               while ( $n-- && false != ( $job = Job::pop() ) ) {
-                       $output = $job->toString() . "\n";
-                       $t = - microtime( true );
-                       $success = $job->run();
-                       $t += microtime( true );
-                       $t = round( $t * 1000 );
-                       if ( !$success ) {
-                               $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
-                       } else {
-                               $output .= "Success, Time: $t ms\n";
-                       }
-                       wfDebugLog( 'jobqueue', $output );
-               }
-       }
-
-       /**
-        * Ends this task peacefully
-        */
-       public function restInPeace() {
-               // Do any deferred jobs
-               DeferredUpdates::doUpdates( 'commit' );
-
-               // Execute a job from the queue
-               $this->doJobs();
-
-               // Log message usage, if $wgAdaptiveMessageCache is set to true
-               MessageCache::logMessages();
-
-               // Log profiling data, e.g. in the database or UDP
-               wfLogProfilingData();
-
-               // Commit and close up!
-               $factory = wfGetLBFactory();
-               $factory->commitMasterChanges();
-               $factory->shutdown();
-
-               wfDebug( "Request ended normally\n" );
-       }
-
        /**
         * Perform one of the "standard" actions
         *
@@ -603,4 +545,62 @@ class MediaWiki {
 
                wfProfileOut( __METHOD__ );
        }
+
+       /**
+        * Ends this task peacefully
+        */
+       public function restInPeace() {
+               // Do any deferred jobs
+               DeferredUpdates::doUpdates( 'commit' );
+
+               // Execute a job from the queue
+               $this->doJobs();
+
+               // Log message usage, if $wgAdaptiveMessageCache is set to true
+               MessageCache::logMessages();
+
+               // Log profiling data, e.g. in the database or UDP
+               wfLogProfilingData();
+
+               // Commit and close up!
+               $factory = wfGetLBFactory();
+               $factory->commitMasterChanges();
+               $factory->shutdown();
+
+               wfDebug( "Request ended normally\n" );
+       }
+
+       /**
+        * Do a job from the job queue
+        */
+       private function doJobs() {
+               global $wgJobRunRate;
+
+               if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
+                       return;
+               }
+               if ( $wgJobRunRate < 1 ) {
+                       $max = mt_getrandmax();
+                       if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
+                               return;
+                       }
+                       $n = 1;
+               } else {
+                       $n = intval( $wgJobRunRate );
+               }
+
+               while ( $n-- && false != ( $job = Job::pop() ) ) {
+                       $output = $job->toString() . "\n";
+                       $t = - microtime( true );
+                       $success = $job->run();
+                       $t += microtime( true );
+                       $t = round( $t * 1000 );
+                       if ( !$success ) {
+                               $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
+                       } else {
+                               $output .= "Success, Time: $t ms\n";
+                       }
+                       wfDebugLog( 'jobqueue', $output );
+               }
+       }
 }
index 0595923..b98f521 100644 (file)
@@ -7061,6 +7061,7 @@ $zh2Hant = array(
 '皇庄' => '皇莊',
 '皓发' => '皓髮',
 '皮制服' => '皮制服',
+'皮肤' => '皮膚',
 '皮里春秋' => '皮裡春秋',
 '皮里阳秋' => '皮裡陽秋',
 '皮制' => '皮製',
@@ -8409,6 +8410,7 @@ $zh2Hant = array(
 '跌扑' => '跌扑',
 '跌荡' => '跌蕩',
 '路签' => '路籤',
+'路面' => '路面',
 '跳梁小丑' => '跳樑小丑',
 '跳荡' => '跳蕩',
 '跳表' => '跳錶',
@@ -18192,7 +18194,6 @@ $zh2CN = array(
 '攜帶型' => '便携式',
 '資訊理論' => '信息论',
 '母音' => '元音',
-'游標' => '光标',
 '光碟' => '光盘',
 '光碟機' => '光驱',
 '柯林頓' => '克林顿',
@@ -18455,4 +18456,4 @@ $zh2SG = array(
 '笨豬跳' => '绑紧跳',
 '蹦极跳' => '绑紧跳',
 '笑星' => '谐星',
-);
\ No newline at end of file
+);
index 99e22d5..d7c341d 100644 (file)
@@ -690,7 +690,7 @@ class ApiQuery extends ApiBase {
                                        'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
                        'redirects' => 'Automatically resolve redirects',
                        'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.",
-                                       'Languages that support variant conversion include gan, iu, kk, ku, shi, sr, tg, zh' ),
+                                       'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
                        'indexpageids' => 'Include an additional pageids section listing all returned page IDs',
                        'export' => 'Export the current revisions of all given or generated pages',
                        'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export',
index e05ffb6..719c84a 100644 (file)
@@ -59,73 +59,86 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
                }
                $images = $namespaces[NS_FILE];
 
-               $this->addTables( 'image', 'i1' );
-               $this->addTables( 'image', 'i2' );
-               $this->addFields( array(
-                       'i1.img_name AS orig_name',
-                       'i2.img_name AS dup_name',
-                       'i2.img_user_text AS dup_user_text',
-                       'i2.img_timestamp AS dup_timestamp'
-               ) );
-
-               $this->addWhere( array(
-                       'i1.img_name' => array_keys( $images ),
-                       'i1.img_sha1 = i2.img_sha1',
-                       'i1.img_name != i2.img_name',
-               ) );
+               if( $params['dir'] == 'descending' ) {
+                       $images = array_reverse( $images );
+               }
 
+               $skipUntilThisDup = false;
                if ( isset( $params['continue'] ) ) {
                        $cont = explode( '|', $params['continue'] );
                        if ( count( $cont ) != 2 ) {
                                $this->dieUsage( 'Invalid continue param. You should pass the ' .
                                        'original value returned by the previous query', '_badcontinue' );
                        }
-                       $op = $params['dir'] == 'descending' ? '<' : '>';
-                       $db = $this->getDB();
-                       $orig = $db->addQuotes( $cont[0] );
-                       $dup = $db->addQuotes( $cont[1] );
-                       $this->addWhere(
-                               "i1.img_name $op $orig OR " .
-                               "(i1.img_name = $orig AND " .
-                               "i2.img_name $op= $dup)"
-                       );
+                       $fromImage = $cont[0];
+                       $skipUntilThisDup = $cont[1];
+                       // Filter out any images before $fromImage
+                       foreach ( $images as $image => $pageId ) {
+                               if ( $image < $fromImage ) {
+                                       unset( $images[$image] );
+                               } else {
+                                       break;
+                               }
+                       }
                }
 
-               $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
-               // Don't order by i1.img_name if it's constant in the WHERE clause
-               if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
-                       $this->addOption( 'ORDER BY', 'i2.img_name' . $sort );
-               } else {
-                       $this->addOption( 'ORDER BY', array(
-                                       'i1.img_name' . $sort,
-                                       'i2.img_name' . $sort
-                       ));
-               }
-               $this->addOption( 'LIMIT', $params['limit'] + 1 );
+               $files = RepoGroup::singleton()->findFiles( array_keys( $images ) );
 
-               $res = $this->select( __METHOD__ );
+               $fit = true;
                $count = 0;
                $titles = array();
-               foreach ( $res as $row ) {
-                       if ( ++$count > $params['limit'] ) {
-                               // We've reached the one extra which shows that
-                               // there are additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
-                               break;
+
+               $sha1s = array();
+               foreach ( $files as $file ) {
+                       $sha1s[$file->getName()] = $file->getSha1();
+               }
+
+               // find all files with the hashes, result format is: array( hash => array( dup1, dup2 ), hash1 => ... )
+               $filesBySha1s = RepoGroup::singleton()->findBySha1s( array_unique( array_values( $sha1s ) ) );
+
+               // iterate over $images to handle continue param correct
+               foreach( $images as $image => $pageId ) {
+                       if( !isset( $sha1s[$image] ) ) {
+                               continue; //file does not exist
                        }
-                       if ( !is_null( $resultPageSet ) ) {
-                               $titles[] = Title::makeTitle( NS_FILE, $row->dup_name );
-                       } else {
-                               $r = array(
-                                       'name' => $row->dup_name,
-                                       'user' => $row->dup_user_text,
-                                       'timestamp' => wfTimestamp( TS_ISO_8601, $row->dup_timestamp )
-                               );
-                               $fit = $this->addPageSubItem( $images[$row->orig_name], $r );
-                               if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
+                       $sha1 = $sha1s[$image];
+                       $dupFiles = $filesBySha1s[$sha1];
+                       if( $params['dir'] == 'descending' ) {
+                               $dupFiles = array_reverse( $dupFiles );
+                       }
+                       foreach ( $dupFiles as $dupFile ) {
+                               $dupName = $dupFile->getName();
+                               if( $image == $dupName ) {
+                                       continue; //ignore the file itself
+                               }
+                               if( $skipUntilThisDup !== false && $dupName < $skipUntilThisDup ) {
+                                       continue; //skip to pos after the image from continue param
+                               }
+                               $skipUntilThisDup = false;
+                               if ( ++$count > $params['limit'] ) {
+                                       $fit = false; //break outer loop
+                                       // We're one over limit which shows that
+                                       // there are additional images to be had. Stop here...
+                                       $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
                                        break;
                                }
+                               if ( !is_null( $resultPageSet ) ) {
+                                       $titles[] = $file->getTitle();
+                               } else {
+                                       $r = array(
+                                               'name' => $dupName,
+                                               'user' => $dupFile->getUser( 'text' ),
+                                               'timestamp' => wfTimestamp( TS_ISO_8601, $dupFile->getTimestamp() )
+                                       );
+                                       $fit = $this->addPageSubItem( $pageId, $r );
+                                       if ( !$fit ) {
+                                               $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
+                                               break;
+                                       }
+                               }
+                       }
+                       if( !$fit ) {
+                               break;
                        }
                }
                if ( !is_null( $resultPageSet ) ) {
@@ -155,7 +168,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
 
        public function getParamDescription() {
                return array(
-                       'limit' => 'How many files to return',
+                       'limit' => 'How many duplicate files to return',
                        'continue' => 'When more results are available, use this to continue',
                        'dir' => 'The direction in which to list',
                );
@@ -172,7 +185,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
        }
 
        public function getDescription() {
-               return 'List all files that are duplicates of the given file(s)';
+               return 'List all files that are duplicates of the given file(s) based on hash values';
        }
 
        public function getPossibleErrors() {
index eed9d7c..a5486ef 100644 (file)
@@ -59,6 +59,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                $fld_mediatype = isset( $prop['mediatype'] );
                $fld_metadata = isset( $prop['metadata'] );
                $fld_bitdepth = isset( $prop['bitdepth'] );
+               $fld_archivename = isset( $prop['archivename'] );
 
                $this->addTables( 'filearchive' );
 
@@ -72,6 +73,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                $this->addFieldsIf( 'fa_media_type', $fld_mediatype );
                $this->addFieldsIf( 'fa_metadata', $fld_metadata );
                $this->addFieldsIf( 'fa_bits', $fld_bitdepth );
+               $this->addFieldsIf( 'fa_archive_name', $fld_archivename );
 
                if ( !is_null( $params['continue'] ) ) {
                        $cont = explode( '|', $params['continue'] );
@@ -194,6 +196,9 @@ class ApiQueryFilearchive extends ApiQueryBase {
                        if ( $fld_mime ) {
                                $file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
                        }
+                       if ( $fld_archivename && !is_null( $row->fa_archive_name ) ) {
+                               $file['archivename'] = $row->fa_archive_name;
+                       }
 
                        if ( $row->fa_deleted & File::DELETED_FILE ) {
                                $file['filehidden'] = '';
@@ -256,7 +261,8 @@ class ApiQueryFilearchive extends ApiQueryBase {
                                        'mime',
                                        'mediatype',
                                        'metadata',
-                                       'bitdepth'
+                                       'bitdepth',
+                                       'archivename',
                                ),
                        ),
                );
@@ -285,6 +291,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                                ' mediatype         - Adds the media type of the image',
                                ' metadata          - Lists EXIF metadata for the version of the image',
                                ' bitdepth          - Adds the bit depth of the version',
+                               ' archivename       - Adds the file name of the archive version for non-latest versions'
                        ),
                );
        }
@@ -346,7 +353,10 @@ class ApiQueryFilearchive extends ApiQueryBase {
                        ),
                        'mediatype' => array(
                                'mediatype' => 'string'
-                       )
+                       ),
+                       'archivename' => array(
+                               'archivename' => 'string'
+                       ),
                );
        }
 
index 95c6bda..4a85b0b 100644 (file)
@@ -435,15 +435,14 @@ class ApiQueryInfo extends ApiQueryBase {
                // Get normal protections for existing titles
                if ( count( $this->titles ) ) {
                        $this->resetQueryParams();
-                       $this->addTables( array( 'page_restrictions', 'page' ) );
-                       $this->addWhere( 'page_id=pr_page' );
+                       $this->addTables( 'page_restrictions' );
                        $this->addFields( array( 'pr_page', 'pr_type', 'pr_level',
-                                       'pr_expiry', 'pr_cascade', 'page_namespace',
-                                       'page_title' ) );
+                                       'pr_expiry', 'pr_cascade' ) );
                        $this->addWhereFld( 'pr_page', array_keys( $this->titles ) );
 
                        $res = $this->select( __METHOD__ );
                        foreach ( $res as $row ) {
+                               $title = $this->titles[$row->pr_page];
                                $a = array(
                                        'type' => $row->pr_type,
                                        'level' => $row->pr_level,
@@ -452,11 +451,14 @@ class ApiQueryInfo extends ApiQueryBase {
                                if ( $row->pr_cascade ) {
                                        $a['cascade'] = '';
                                }
-                               $this->protections[$row->page_namespace][$row->page_title][] = $a;
-
-                               // Also check old restrictions
-                               if ( $this->pageRestrictions[$row->pr_page] ) {
-                                       $restrictions = explode( ':', trim( $this->pageRestrictions[$row->pr_page] ) );
+                               $this->protections[$title->getNamespace()][$title->getDBkey()][] = $a;
+                       }
+                       // Also check old restrictions
+                       foreach( $this->titles as $pageId => $title ) {
+                               if ( $this->pageRestrictions[$pageId] ) {
+                                       $namespace = $title->getNamespace();
+                                       $dbKey = $title->getDBkey();
+                                       $restrictions = explode( ':', trim( $this->pageRestrictions[$pageId] ) );
                                        foreach ( $restrictions as $restrict ) {
                                                $temp = explode( '=', trim( $restrict ) );
                                                if ( count( $temp ) == 1 ) {
@@ -466,12 +468,12 @@ class ApiQueryInfo extends ApiQueryBase {
                                                        if ( $restriction == '' ) {
                                                                continue;
                                                        }
-                                                       $this->protections[$row->page_namespace][$row->page_title][] = array(
+                                                       $this->protections[$namespace][$dbKey][] = array(
                                                                'type' => 'edit',
                                                                'level' => $restriction,
                                                                'expiry' => 'infinity',
                                                        );
-                                                       $this->protections[$row->page_namespace][$row->page_title][] = array(
+                                                       $this->protections[$namespace][$dbKey][] = array(
                                                                'type' => 'move',
                                                                'level' => $restriction,
                                                                'expiry' => 'infinity',
@@ -481,7 +483,7 @@ class ApiQueryInfo extends ApiQueryBase {
                                                        if ( $restriction == '' ) {
                                                                continue;
                                                        }
-                                                       $this->protections[$row->page_namespace][$row->page_title][] = array(
+                                                       $this->protections[$namespace][$dbKey][] = array(
                                                                'type' => $temp[0],
                                                                'level' => $restriction,
                                                                'expiry' => 'infinity',
index 2f11570..bc514b3 100644 (file)
@@ -367,7 +367,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
        }
 
        public function getAllowedParams() {
-               global $wgLogTypes, $wgLogActions;
+               global $wgLogTypes, $wgLogActions, $wgLogActionsHandlers;
                return array(
                        'prop' => array(
                                ApiBase::PARAM_ISMULTI => true,
@@ -389,7 +389,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
                                ApiBase::PARAM_TYPE => $wgLogTypes
                        ),
                        'action' => array(
-                               ApiBase::PARAM_TYPE => array_keys( $wgLogActions )
+                               ApiBase::PARAM_TYPE => array_keys( array_merge( $wgLogActions, $wgLogActionsHandlers ) )
                        ),
                        'start' => array(
                                ApiBase::PARAM_TYPE => 'timestamp'
index a530620..e99ba6c 100644 (file)
@@ -5,6 +5,8 @@
  * aims to be both simple and very flexible. It is centered around an associative
  * array of fields and various methods to do common interaction with the database.
  *
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
index 684f4b4..99413f9 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 /**
  * Interface for objects representing a single database table.
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 3f2d803..2a5837a 100644 (file)
@@ -3,6 +3,8 @@
  * ORMIterator that takes a ResultWrapper object returned from
  * a select operation returning IORMRow objects (ie IORMTable::select).
  *
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
index 6f19fe1..303f3a2 100644 (file)
@@ -5,6 +5,8 @@
  * aims to be both simple and very flexible. It is centered around an associative
  * array of fields and various methods to do common interaction with the database.
  *
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
index 130fdf9..a4396af 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 /**
  * Abstract base class for representing a single database table.
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -559,7 +560,7 @@ abstract class ORMTable implements IORMTable {
         * @return IORMTable
         */
        public static function singleton() {
-               $class = function_exists( 'get_called_class' ) ? get_called_class() : self::get_called_class();
+               $class = get_called_class();
 
                if ( !array_key_exists( $class, self::$instanceCache ) ) {
                        self::$instanceCache[$class] = new $class;
@@ -568,36 +569,6 @@ abstract class ORMTable implements IORMTable {
                return self::$instanceCache[$class];
        }
 
-       /**
-        * Compatibility fallback function so the singleton method works on PHP < 5.3.
-        * Code borrowed from http://www.php.net/manual/en/function.get-called-class.php#107445
-        *
-        * @since 1.20
-        *
-        * @return string
-        */
-       protected static function get_called_class() {
-               $bt = debug_backtrace();
-               $l = count($bt) - 1;
-               $matches = array();
-               while(empty($matches) && $l > -1){
-                       $lines = file($bt[$l]['file']);
-                       $callerLine = $lines[$bt[$l]['line']-1];
-                       preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l--]['function'].'/',
-                               $callerLine,
-                               $matches);
-               }
-               if (!isset($matches[1])) $matches[1]=NULL; //for notices
-               if ($matches[1] == 'self') {
-                       $line = $bt[$l]['line']-1;
-                       while ($line > 0 && strpos($lines[$line], 'class') === false) {
-                               $line--;
-                       }
-                       preg_match('/class[\s]+(.+?)[\s]+/si', $lines[$line], $matches);
-               }
-               return $matches[1];
-       }
-
        /**
         * Get an array with fields from a database result,
         * that can be fed directly to the constructor or
@@ -620,7 +591,7 @@ abstract class ORMTable implements IORMTable {
        /**
         * @see ORMTable::newRowFromFromDBResult
         *
-        * @deprecated use newRowFromFromDBResult instead
+        * @deprecated use newRowFromDBResult instead
         * @since 1.20
         *
         * @param stdClass $result
index 3d2ccf4..5a55096 100644 (file)
@@ -439,6 +439,24 @@ class FileRepo {
                return array();
        }
 
+       /**
+        * Get an array of arrays or iterators of file objects for files that
+        * have the given SHA-1 content hashes.
+        *
+        * @param $hashes array An array of hashes
+        * @return array An Array of arrays or iterators of file objects and the hash as key
+        */
+       public function findBySha1s( array $hashes ) {
+               $result = array();
+               foreach ( $hashes as $hash ) {
+                       $files = $this->findBySha1( $hash );
+                       if ( count( $files ) ) {
+                               $result[$hash] = $files;
+                       }
+               }
+               return $result;
+       }
+
        /**
         * Get the public root URL of the repository
         *
index dd0c947..0954422 100644 (file)
@@ -248,6 +248,39 @@ class LocalRepo extends FileRepo {
                return $result;
        }
 
+       /**
+        * Get an array of arrays or iterators of file objects for files that
+        * have the given SHA-1 content hashes.
+        *
+        * Overrides generic implementation in FileRepo for performance reason
+        *
+        * @param $hashes array An array of hashes
+        * @return array An Array of arrays or iterators of file objects and the hash as key
+        */
+       function findBySha1s( array $hashes ) {
+               if( !count( $hashes ) ) {
+                       return array(); //empty parameter
+               }
+
+               $dbr = $this->getSlaveDB();
+               $res = $dbr->select(
+                       'image',
+                       LocalFile::selectFields(),
+                       array( 'img_sha1' => $hashes ),
+                       __METHOD__,
+                       array( 'ORDER BY' => 'img_name' )
+               );
+
+               $result = array();
+               foreach ( $res as $row ) {
+                       $file = $this->newFileFromRow( $row );
+                       $result[$file->getSha1()][] = $file;
+               }
+               $res->free();
+
+               return $result;
+       }
+
        /**
         * Get a connection to the slave DB
         * @return DatabaseBase
index 6b31b7e..f9e5759 100644 (file)
@@ -263,6 +263,28 @@ class RepoGroup {
                return $result;
        }
 
+       /**
+        * Find all instances of files with this keys
+        *
+        * @param $hashes Array base 36 SHA-1 hashes
+        * @return Array of array of File objects
+        */
+       function findBySha1s( array $hashes ) {
+               if ( !$this->reposInitialised ) {
+                       $this->initialiseRepos();
+               }
+
+               $result = $this->localRepo->findBySha1s( $hashes );
+               foreach ( $this->foreignRepos as $repo ) {
+                       $result = array_merge_recursive( $result, $repo->findBySha1s( $hashes ) );
+               }
+               //sort the merged (and presorted) sublist of each hash
+               foreach( $result as $hash => $files ) {
+                       usort( $result[$hash], 'File::compare' );
+               }
+               return $result;
+       }
+
        /**
         * Get the repo instance with a given key.
         * @param $index string|int
index 94ddb44..5f82a90 100644 (file)
@@ -1058,7 +1058,7 @@ class SwiftFileBackend extends FileBackendStore {
         *                          Setting this to '*' effectively makes a container public.
         *   - .rlistings:<regex> : Grants access if the request is from a referrer host that
         *                          matches the expression and the request for a listing.
-        * 
+        *
         * $writeGrps is a list of the possible criteria for a request to have
         * access to write to a container. Each item is of the following format:
         *   - account:user       : Grants access if the request is by the given user
@@ -1296,7 +1296,7 @@ abstract class SwiftFileBackendList implements Iterator {
        protected $dir; // string; storage directory
        protected $suffixStart; // integer
 
-       const PAGE_SIZE = 5000; // file listing buffer size
+       const PAGE_SIZE = 9000; // file listing buffer size
 
        /**
         * @param $backend SwiftFileBackend
diff --git a/includes/media/Generic.php b/includes/media/Generic.php
deleted file mode 100644 (file)
index bd4d865..0000000
+++ /dev/null
@@ -1,780 +0,0 @@
-<?php
-/**
- * Media-handling base classes and generic functionality.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Media
- */
-
-/**
- * Base media handler class
- *
- * @ingroup Media
- */
-abstract class MediaHandler {
-       const TRANSFORM_LATER = 1;
-       const METADATA_GOOD = true;
-       const METADATA_BAD = false;
-       const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
-       /**
-        * Instance cache
-        */
-       static $handlers = array();
-
-       /**
-        * Get a MediaHandler for a given MIME type from the instance cache
-        *
-        * @param $type string
-        *
-        * @return MediaHandler
-        */
-       static function getHandler( $type ) {
-               global $wgMediaHandlers;
-               if ( !isset( $wgMediaHandlers[$type] ) ) {
-                       wfDebug( __METHOD__ . ": no handler found for $type.\n");
-                       return false;
-               }
-               $class = $wgMediaHandlers[$type];
-               if ( !isset( self::$handlers[$class] ) ) {
-                       self::$handlers[$class] = new $class;
-                       if ( !self::$handlers[$class]->isEnabled() ) {
-                               self::$handlers[$class] = false;
-                       }
-               }
-               return self::$handlers[$class];
-       }
-
-       /**
-        * Get an associative array mapping magic word IDs to parameter names.
-        * Will be used by the parser to identify parameters.
-        */
-       abstract function getParamMap();
-
-       /**
-        * Validate a thumbnail parameter at parse time.
-        * Return true to accept the parameter, and false to reject it.
-        * If you return false, the parser will do something quiet and forgiving.
-        *
-        * @param $name
-        * @param $value
-        */
-       abstract function validateParam( $name, $value );
-
-       /**
-        * Merge a parameter array into a string appropriate for inclusion in filenames
-        *
-        * @param $params array
-        */
-       abstract function makeParamString( $params );
-
-       /**
-        * Parse a param string made with makeParamString back into an array
-        *
-        * @param $str string
-        */
-       abstract function parseParamString( $str );
-
-       /**
-        * Changes the parameter array as necessary, ready for transformation.
-        * Should be idempotent.
-        * Returns false if the parameters are unacceptable and the transform should fail
-        * @param $image
-        * @param $params
-        */
-       abstract function normaliseParams( $image, &$params );
-
-       /**
-        * Get an image size array like that returned by getimagesize(), or false if it
-        * can't be determined.
-        *
-        * @param $image File: the image object, or false if there isn't one
-        * @param $path String: the filename
-        * @return Array Follow the format of PHP getimagesize() internal function. See http://www.php.net/getimagesize
-        */
-       abstract function getImageSize( $image, $path );
-
-       /**
-        * Get handler-specific metadata which will be saved in the img_metadata field.
-        *
-        * @param $image File: the image object, or false if there isn't one.
-        *   Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
-        * @param $path String: the filename
-        * @return String
-        */
-       function getMetadata( $image, $path ) { return ''; }
-
-       /**
-       * Get metadata version.
-       *
-       * This is not used for validating metadata, this is used for the api when returning
-       * metadata, since api content formats should stay the same over time, and so things
-       * using ForiegnApiRepo can keep backwards compatibility
-       *
-       * All core media handlers share a common version number, and extensions can
-       * use the GetMetadataVersion hook to append to the array (they should append a unique
-       * string so not to get confusing). If there was a media handler named 'foo' with metadata
-       * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
-       * version is 2, the end version string would look like '2;foo=3'.
-       *
-       * @return string version string
-       */
-       static function getMetadataVersion () {
-               $version = Array( '2' ); // core metadata version
-               wfRunHooks('GetMetadataVersion', Array(&$version));
-               return implode( ';', $version);
-        }
-
-       /**
-       * Convert metadata version.
-       *
-       * By default just returns $metadata, but can be used to allow
-       * media handlers to convert between metadata versions.
-       *
-       * @param $metadata Mixed String or Array metadata array (serialized if string)
-       * @param $version Integer target version
-       * @return Array serialized metadata in specified version, or $metadata on fail.
-       */
-       function convertMetadataVersion( $metadata, $version = 1 ) {
-               if ( !is_array( $metadata ) ) {
-
-                       //unserialize to keep return parameter consistent.
-                       wfSuppressWarnings();
-                       $ret = unserialize( $metadata );
-                       wfRestoreWarnings();
-                       return $ret;
-               }
-               return $metadata;
-       }
-
-       /**
-        * Get a string describing the type of metadata, for display purposes.
-        *
-        * @return string
-        */
-       function getMetadataType( $image ) { return false; }
-
-       /**
-        * Check if the metadata string is valid for this handler.
-        * If it returns MediaHandler::METADATA_BAD (or false), Image
-        * will reload the metadata from the file and update the database.
-        * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
-        * MediaHanlder::METADATA_COMPATIBLE if metadata is old but backwards
-        * compatible (which may or may not trigger a metadata reload).
-        * @return bool
-        */
-       function isMetadataValid( $image, $metadata ) {
-               return self::METADATA_GOOD;
-       }
-
-
-       /**
-        * Get a MediaTransformOutput object representing an alternate of the transformed
-        * output which will call an intermediary thumbnail assist script.
-        *
-        * Used when the repository has a thumbnailScriptUrl option configured.
-        *
-        * Return false to fall back to the regular getTransform().
-        * @return bool
-        */
-       function getScriptedTransform( $image, $script, $params ) {
-               return false;
-       }
-
-       /**
-        * Get a MediaTransformOutput object representing the transformed output. Does not
-        * actually do the transform.
-        *
-        * @param $image File: the image object
-        * @param $dstPath String: filesystem destination path
-        * @param $dstUrl String: Destination URL to use in output HTML
-        * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
-        * @return MediaTransformOutput
-        */
-       final function getTransform( $image, $dstPath, $dstUrl, $params ) {
-               return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
-       }
-
-       /**
-        * Get a MediaTransformOutput object representing the transformed output. Does the
-        * transform unless $flags contains self::TRANSFORM_LATER.
-        *
-        * @param $image File: the image object
-        * @param $dstPath String: filesystem destination path
-        * @param $dstUrl String: destination URL to use in output HTML
-        * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
-        * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
-        *
-        * @return MediaTransformOutput
-        */
-       abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
-
-       /**
-        * Get the thumbnail extension and MIME type for a given source MIME type
-        * @return array thumbnail extension and MIME type
-        */
-       function getThumbType( $ext, $mime, $params = null ) {
-               $magic = MimeMagic::singleton();
-               if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
-                       // The extension is not valid for this mime type and we do
-                       // recognize the mime type
-                       $extensions = $magic->getExtensionsForType( $mime );
-                       if ( $extensions ) {
-                               return array( strtok( $extensions, ' ' ), $mime );
-                       }
-               }
-
-               // The extension is correct (true) or the mime type is unknown to
-               // MediaWiki (null)
-               return array( $ext, $mime );
-       }
-
-       /**
-        * True if the handled types can be transformed
-        * @return bool
-        */
-       function canRender( $file ) { return true; }
-       /**
-        * True if handled types cannot be displayed directly in a browser
-        * but can be rendered
-        * @return bool
-        */
-       function mustRender( $file ) { return false; }
-       /**
-        * True if the type has multi-page capabilities
-        * @return bool
-        */
-       function isMultiPage( $file ) { return false; }
-       /**
-        * Page count for a multi-page document, false if unsupported or unknown
-        * @return bool
-        */
-       function pageCount( $file ) { return false; }
-       /**
-        * The material is vectorized and thus scaling is lossless
-        * @return bool
-        */
-       function isVectorized( $file ) { return false; }
-       /**
-        * False if the handler is disabled for all files
-        * @return bool
-        */
-       function isEnabled() { return true; }
-
-       /**
-        * Get an associative array of page dimensions
-        * Currently "width" and "height" are understood, but this might be
-        * expanded in the future.
-        * Returns false if unknown or if the document is not multi-page.
-        *
-        * @param $image File
-        * @param $page Unused, left for backcompatibility?
-        * @return array
-        */
-       function getPageDimensions( $image, $page ) {
-               $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
-               return array(
-                       'width' => $gis[0],
-                       'height' => $gis[1]
-               );
-       }
-
-       /**
-        * Generic getter for text layer.
-        * Currently overloaded by PDF and DjVu handlers
-        * @return bool
-        */
-       function getPageText( $image, $page ) {
-               return false;
-       }
-
-       /**
-        * Get an array structure that looks like this:
-        *
-        * array(
-        *    'visible' => array(
-        *       'Human-readable name' => 'Human readable value',
-        *       ...
-        *    ),
-        *    'collapsed' => array(
-        *       'Human-readable name' => 'Human readable value',
-        *       ...
-        *    )
-        * )
-        * The UI will format this into a table where the visible fields are always
-        * visible, and the collapsed fields are optionally visible.
-        *
-        * The function should return false if there is no metadata to display.
-        */
-
-       /**
-        * @todo FIXME: I don't really like this interface, it's not very flexible
-        * I think the media handler should generate HTML instead. It can do
-        * all the formatting according to some standard. That makes it possible
-        * to do things like visual indication of grouped and chained streams
-        * in ogg container files.
-        * @return bool
-        */
-       function formatMetadata( $image ) {
-               return false;
-       }
-
-       /** sorts the visible/invisible field.
-        * Split off from ImageHandler::formatMetadata, as used by more than
-        * one type of handler.
-        *
-        * This is used by the media handlers that use the FormatMetadata class
-        *
-        * @param $metadataArray Array metadata array
-        * @return array for use displaying metadata.
-        */
-       function formatMetadataHelper( $metadataArray ) {
-                $result = array(
-                       'visible' => array(),
-                       'collapsed' => array()
-               );
-
-               $formatted = FormatMetadata::getFormattedData( $metadataArray );
-               // Sort fields into visible and collapsed
-               $visibleFields = $this->visibleMetadataFields();
-               foreach ( $formatted as $name => $value ) {
-                       $tag = strtolower( $name );
-                       self::addMeta( $result,
-                               in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
-                               'exif',
-                               $tag,
-                               $value
-                       );
-               }
-               return $result;
-       }
-
-       /**
-        * Get a list of metadata items which should be displayed when
-        * the metadata table is collapsed.
-        *
-        * @return array of strings
-        * @access protected
-        */
-       function visibleMetadataFields() {
-               $fields = array();
-               $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
-               foreach( $lines as $line ) {
-                       $matches = array();
-                       if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
-                               $fields[] = $matches[1];
-                       }
-               }
-               $fields = array_map( 'strtolower', $fields );
-               return $fields;
-       }
-
-
-       /**
-        * This is used to generate an array element for each metadata value
-        * That array is then used to generate the table of metadata values
-        * on the image page
-        *
-        * @param &$array Array An array containing elements for each type of visibility
-        * and each of those elements being an array of metadata items. This function adds
-        * a value to that array.
-        * @param $visibility string ('visible' or 'collapsed') if this value is hidden
-        * by default.
-        * @param $type String type of metadata tag (currently always 'exif')
-        * @param $id String the name of the metadata tag (like 'artist' for example).
-        * its name in the table displayed is the message "$type-$id" (Ex exif-artist ).
-        * @param $value String thingy goes into a wikitext table; it used to be escaped but
-        * that was incompatible with previous practise of customized display
-        * with wikitext formatting via messages such as 'exif-model-value'.
-        * So the escaping is taken back out, but generally this seems a confusing
-        * interface.
-        * @param $param String value to pass to the message for the name of the field
-        * as $1. Currently this parameter doesn't seem to ever be used.
-        *
-        * Note, everything here is passed through the parser later on (!)
-        */
-       protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
-               $msg = wfMessage( "$type-$id", $param );
-               if ( $msg->exists() ) {
-                       $name = $msg->text();
-               } else {
-                       // This is for future compatibility when using instant commons.
-                       // So as to not display as ugly a name if a new metadata
-                       // property is defined that we don't know about
-                       // (not a major issue since such a property would be collapsed
-                       // by default).
-                       wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id . "\n" );
-                       $name = wfEscapeWikiText( $id );
-               }
-               $array[$visibility][] = array(
-                       'id' => "$type-$id",
-                       'name' => $name,
-                       'value' => $value
-               );
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       function getShortDesc( $file ) {
-               global $wgLang;
-               return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       function getLongDesc( $file ) {
-               global $wgLang;
-               return wfMessage( 'file-info', htmlspecialchars( $wgLang->formatSize( $file->getSize() ) ),
-                       $file->getMimeType() )->parse();
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       static function getGeneralShortDesc( $file ) {
-               global $wgLang;
-               return $wgLang->formatSize( $file->getSize() );
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       static function getGeneralLongDesc( $file ) {
-               global $wgLang;
-               return wfMessage( 'file-info', $wgLang->formatSize( $file->getSize() ),
-                       $file->getMimeType() )->parse();
-       }
-
-       /**
-        * Calculate the largest thumbnail width for a given original file size
-        * such that the thumbnail's height is at most $maxHeight.
-        * @param $boxWidth Integer Width of the thumbnail box.
-        * @param $boxHeight Integer Height of the thumbnail box.
-        * @param $maxHeight Integer Maximum height expected for the thumbnail.
-        * @return Integer.
-        */
-       public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
-               $idealWidth = $boxWidth * $maxHeight / $boxHeight;
-               $roundedUp = ceil( $idealWidth );
-               if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
-                       return floor( $idealWidth );
-               } else {
-                       return $roundedUp;
-               }
-       }
-
-       function getDimensionsString( $file ) {
-               return '';
-       }
-
-       /**
-        * Modify the parser object post-transform
-        */
-       function parserTransformHook( $parser, $file ) {}
-
-       /**
-        * File validation hook called on upload.
-        *
-        * If the file at the given local path is not valid, or its MIME type does not
-        * match the handler class, a Status object should be returned containing
-        * relevant errors.
-        *
-        * @param $fileName string The local path to the file.
-        * @return Status object
-        */
-       function verifyUpload( $fileName ) {
-               return Status::newGood();
-       }
-
-       /**
-        * Check for zero-sized thumbnails. These can be generated when
-        * no disk space is available or some other error occurs
-        *
-        * @param $dstPath string The location of the suspect file
-        * @param $retval int Return value of some shell process, file will be deleted if this is non-zero
-        * @return bool True if removed, false otherwise
-        */
-       function removeBadFile( $dstPath, $retval = 0 ) {
-               if( file_exists( $dstPath ) ) {
-                       $thumbstat = stat( $dstPath );
-                       if( $thumbstat['size'] == 0 || $retval != 0 ) {
-                               $result = unlink( $dstPath );
-
-                               if ( $result ) {
-                                       wfDebugLog( 'thumbnail',
-                                               sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
-                                                       $thumbstat['size'], $dstPath ) );
-                               } else {
-                                       wfDebugLog( 'thumbnail',
-                                               sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
-                                                       $thumbstat['size'], $dstPath ) );
-                               }
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Remove files from the purge list
-        *
-        * @param array $files
-        * @param array $options
-        */
-       public function filterThumbnailPurgeList( &$files, $options ) {
-               // Do nothing
-       }
-}
-
-/**
- * Media handler abstract base class for images
- *
- * @ingroup Media
- */
-abstract class ImageHandler extends MediaHandler {
-
-       /**
-        * @param $file File
-        * @return bool
-        */
-       function canRender( $file ) {
-               return ( $file->getWidth() && $file->getHeight() );
-       }
-
-       function getParamMap() {
-               return array( 'img_width' => 'width' );
-       }
-
-       function validateParam( $name, $value ) {
-               if ( in_array( $name, array( 'width', 'height' ) ) ) {
-                       if ( $value <= 0 ) {
-                               return false;
-                       } else {
-                               return true;
-                       }
-               } else {
-                       return false;
-               }
-       }
-
-       function makeParamString( $params ) {
-               if ( isset( $params['physicalWidth'] ) ) {
-                       $width = $params['physicalWidth'];
-               } elseif ( isset( $params['width'] ) ) {
-                       $width = $params['width'];
-               } else {
-                       throw new MWException( 'No width specified to '.__METHOD__ );
-               }
-               # Removed for ProofreadPage
-               #$width = intval( $width );
-               return "{$width}px";
-       }
-
-       function parseParamString( $str ) {
-               $m = false;
-               if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
-                       return array( 'width' => $m[1] );
-               } else {
-                       return false;
-               }
-       }
-
-       function getScriptParams( $params ) {
-               return array( 'width' => $params['width'] );
-       }
-
-       /**
-        * @param $image File
-        * @param  $params
-        * @return bool
-        */
-       function normaliseParams( $image, &$params ) {
-               $mimeType = $image->getMimeType();
-
-               if ( !isset( $params['width'] ) ) {
-                       return false;
-               }
-
-               if ( !isset( $params['page'] ) ) {
-                       $params['page'] = 1;
-               } else  {
-                       if ( $params['page'] > $image->pageCount() ) {
-                               $params['page'] = $image->pageCount();
-                       }
-
-                       if ( $params['page'] < 1 ) {
-                               $params['page'] = 1;
-                       }
-               }
-
-               $srcWidth = $image->getWidth( $params['page'] );
-               $srcHeight = $image->getHeight( $params['page'] );
-
-               if ( isset( $params['height'] ) && $params['height'] != -1 ) {
-                       # Height & width were both set
-                       if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
-                               # Height is the relative smaller dimension, so scale width accordingly
-                               $params['width'] = self::fitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
-
-                               if ( $params['width'] == 0 ) {
-                                       # Very small image, so we need to rely on client side scaling :(
-                                       $params['width'] = 1;
-                               }
-
-                               $params['physicalWidth'] = $params['width'];
-                       } else {
-                               # Height was crap, unset it so that it will be calculated later
-                               unset( $params['height'] );
-                       }
-               }
-
-               if ( !isset( $params['physicalWidth'] ) ) {
-                       # Passed all validations, so set the physicalWidth
-                       $params['physicalWidth'] = $params['width'];
-               }
-
-               # Because thumbs are only referred to by width, the height always needs
-               # to be scaled by the width to keep the thumbnail sizes consistent,
-               # even if it was set inside the if block above
-               $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight,
-                       $params['physicalWidth'] );
-
-               # Set the height if it was not validated in the if block higher up
-               if ( !isset( $params['height'] ) || $params['height'] == -1 ) {
-                       $params['height'] = $params['physicalHeight'];
-               }
-
-
-               if ( !$this->validateThumbParams( $params['physicalWidth'],
-                               $params['physicalHeight'], $srcWidth, $srcHeight, $mimeType ) ) {
-                       return false;
-               }
-               return true;
-       }
-
-       /**
-        * Validate thumbnail parameters and fill in the correct height
-        *
-        * @param $width Integer: specified width (input/output)
-        * @param $height Integer: height (output only)
-        * @param $srcWidth Integer: width of the source image
-        * @param $srcHeight Integer: height of the source image
-        * @param $mimeType
-        * @return bool False to indicate that an error should be returned to the user.
-        */
-       function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
-               $width = intval( $width );
-
-               # Sanity check $width
-               if( $width <= 0) {
-                       wfDebug( __METHOD__.": Invalid destination width: $width\n" );
-                       return false;
-               }
-               if ( $srcWidth <= 0 ) {
-                       wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
-                       return false;
-               }
-
-               $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
-               if ( $height == 0 ) {
-                       # Force height to be at least 1 pixel
-                       $height = 1;
-               }
-               return true;
-       }
-
-       /**
-        * @param $image File
-        * @param  $script
-        * @param  $params
-        * @return bool|ThumbnailImage
-        */
-       function getScriptedTransform( $image, $script, $params ) {
-               if ( !$this->normaliseParams( $image, $params ) ) {
-                       return false;
-               }
-               $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
-               $page = isset( $params['page'] ) ? $params['page'] : false;
-
-               if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
-                       return new ThumbnailImage( $image,
-                               $url, $params['width'], $params['height'], false, $page );
-               }
-       }
-
-       function getImageSize( $image, $path ) {
-               wfSuppressWarnings();
-               $gis = getimagesize( $path );
-               wfRestoreWarnings();
-               return $gis;
-       }
-
-       function isAnimatedImage( $image ) {
-               return false;
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       function getShortDesc( $file ) {
-               global $wgLang;
-               $nbytes = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
-               $widthheight = wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->escaped();
-
-               return "$widthheight ($nbytes)";
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       function getLongDesc( $file ) {
-               global $wgLang;
-               $pages = $file->pageCount();
-               $size = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
-               if ( $pages === false || $pages <= 1 ) {
-                       $msg = wfMessage( 'file-info-size' )->numParams( $file->getWidth(),
-                               $file->getHeight() )->params( $size,
-                               $file->getMimeType() )->parse();
-               } else {
-                       $msg = wfMessage( 'file-info-size-pages' )->numParams( $file->getWidth(),
-                               $file->getHeight() )->params( $size,
-                               $file->getMimeType() )->numParams( $pages )->parse();
-               }
-               return $msg;
-       }
-
-       /**
-        * @param $file File
-        * @return string
-        */
-       function getDimensionsString( $file ) {
-               $pages = $file->pageCount();
-               if ( $pages > 1 ) {
-                       return wfMessage( 'widthheightpage' )->numParams( $file->getWidth(), $file->getHeight(), $pages )->text();
-               } else {
-                       return wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->text();
-               }
-       }
-}
diff --git a/includes/media/ImageHandler.php b/includes/media/ImageHandler.php
new file mode 100644 (file)
index 0000000..69f51be
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Media
+ */
+
+/**
+ * Media handler abstract base class for images
+ *
+ * @ingroup Media
+ */
+abstract class ImageHandler extends MediaHandler {
+
+       /**
+        * @param $file File
+        * @return bool
+        */
+       function canRender( $file ) {
+               return ( $file->getWidth() && $file->getHeight() );
+       }
+
+       function getParamMap() {
+               return array( 'img_width' => 'width' );
+       }
+
+       function validateParam( $name, $value ) {
+               if ( in_array( $name, array( 'width', 'height' ) ) ) {
+                       if ( $value <= 0 ) {
+                               return false;
+                       } else {
+                               return true;
+                       }
+               } else {
+                       return false;
+               }
+       }
+
+       function makeParamString( $params ) {
+               if ( isset( $params['physicalWidth'] ) ) {
+                       $width = $params['physicalWidth'];
+               } elseif ( isset( $params['width'] ) ) {
+                       $width = $params['width'];
+               } else {
+                       throw new MWException( 'No width specified to '.__METHOD__ );
+               }
+               # Removed for ProofreadPage
+               #$width = intval( $width );
+               return "{$width}px";
+       }
+
+       function parseParamString( $str ) {
+               $m = false;
+               if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+                       return array( 'width' => $m[1] );
+               } else {
+                       return false;
+               }
+       }
+
+       function getScriptParams( $params ) {
+               return array( 'width' => $params['width'] );
+       }
+
+       /**
+        * @param $image File
+        * @param  $params
+        * @return bool
+        */
+       function normaliseParams( $image, &$params ) {
+               $mimeType = $image->getMimeType();
+
+               if ( !isset( $params['width'] ) ) {
+                       return false;
+               }
+
+               if ( !isset( $params['page'] ) ) {
+                       $params['page'] = 1;
+               } else  {
+                       if ( $params['page'] > $image->pageCount() ) {
+                               $params['page'] = $image->pageCount();
+                       }
+
+                       if ( $params['page'] < 1 ) {
+                               $params['page'] = 1;
+                       }
+               }
+
+               $srcWidth = $image->getWidth( $params['page'] );
+               $srcHeight = $image->getHeight( $params['page'] );
+
+               if ( isset( $params['height'] ) && $params['height'] != -1 ) {
+                       # Height & width were both set
+                       if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
+                               # Height is the relative smaller dimension, so scale width accordingly
+                               $params['width'] = self::fitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
+
+                               if ( $params['width'] == 0 ) {
+                                       # Very small image, so we need to rely on client side scaling :(
+                                       $params['width'] = 1;
+                               }
+
+                               $params['physicalWidth'] = $params['width'];
+                       } else {
+                               # Height was crap, unset it so that it will be calculated later
+                               unset( $params['height'] );
+                       }
+               }
+
+               if ( !isset( $params['physicalWidth'] ) ) {
+                       # Passed all validations, so set the physicalWidth
+                       $params['physicalWidth'] = $params['width'];
+               }
+
+               # Because thumbs are only referred to by width, the height always needs
+               # to be scaled by the width to keep the thumbnail sizes consistent,
+               # even if it was set inside the if block above
+               $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight,
+                       $params['physicalWidth'] );
+
+               # Set the height if it was not validated in the if block higher up
+               if ( !isset( $params['height'] ) || $params['height'] == -1 ) {
+                       $params['height'] = $params['physicalHeight'];
+               }
+
+
+               if ( !$this->validateThumbParams( $params['physicalWidth'],
+                               $params['physicalHeight'], $srcWidth, $srcHeight, $mimeType ) ) {
+                       return false;
+               }
+               return true;
+       }
+
+       /**
+        * Validate thumbnail parameters and fill in the correct height
+        *
+        * @param $width Integer: specified width (input/output)
+        * @param $height Integer: height (output only)
+        * @param $srcWidth Integer: width of the source image
+        * @param $srcHeight Integer: height of the source image
+        * @param $mimeType
+        * @return bool False to indicate that an error should be returned to the user.
+        */
+       function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
+               $width = intval( $width );
+
+               # Sanity check $width
+               if( $width <= 0) {
+                       wfDebug( __METHOD__.": Invalid destination width: $width\n" );
+                       return false;
+               }
+               if ( $srcWidth <= 0 ) {
+                       wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
+                       return false;
+               }
+
+               $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
+               if ( $height == 0 ) {
+                       # Force height to be at least 1 pixel
+                       $height = 1;
+               }
+               return true;
+       }
+
+       /**
+        * @param $image File
+        * @param  $script
+        * @param  $params
+        * @return bool|ThumbnailImage
+        */
+       function getScriptedTransform( $image, $script, $params ) {
+               if ( !$this->normaliseParams( $image, $params ) ) {
+                       return false;
+               }
+               $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
+               $page = isset( $params['page'] ) ? $params['page'] : false;
+
+               if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
+                       return new ThumbnailImage( $image,
+                               $url, $params['width'], $params['height'], false, $page );
+               }
+       }
+
+       function getImageSize( $image, $path ) {
+               wfSuppressWarnings();
+               $gis = getimagesize( $path );
+               wfRestoreWarnings();
+               return $gis;
+       }
+
+       function isAnimatedImage( $image ) {
+               return false;
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       function getShortDesc( $file ) {
+               global $wgLang;
+               $nbytes = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+               $widthheight = wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->escaped();
+
+               return "$widthheight ($nbytes)";
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       function getLongDesc( $file ) {
+               global $wgLang;
+               $pages = $file->pageCount();
+               $size = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+               if ( $pages === false || $pages <= 1 ) {
+                       $msg = wfMessage( 'file-info-size' )->numParams( $file->getWidth(),
+                               $file->getHeight() )->params( $size,
+                               $file->getMimeType() )->parse();
+               } else {
+                       $msg = wfMessage( 'file-info-size-pages' )->numParams( $file->getWidth(),
+                               $file->getHeight() )->params( $size,
+                               $file->getMimeType() )->numParams( $pages )->parse();
+               }
+               return $msg;
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       function getDimensionsString( $file ) {
+               $pages = $file->pageCount();
+               if ( $pages > 1 ) {
+                       return wfMessage( 'widthheightpage' )->numParams( $file->getWidth(), $file->getHeight(), $pages )->text();
+               } else {
+                       return wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->text();
+               }
+       }
+}
diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php
new file mode 100644 (file)
index 0000000..e883b7f
--- /dev/null
@@ -0,0 +1,547 @@
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Media
+ */
+
+/**
+ * Base media handler class
+ *
+ * @ingroup Media
+ */
+abstract class MediaHandler {
+       const TRANSFORM_LATER = 1;
+       const METADATA_GOOD = true;
+       const METADATA_BAD = false;
+       const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
+       /**
+        * Instance cache
+        */
+       static $handlers = array();
+
+       /**
+        * Get a MediaHandler for a given MIME type from the instance cache
+        *
+        * @param $type string
+        *
+        * @return MediaHandler
+        */
+       static function getHandler( $type ) {
+               global $wgMediaHandlers;
+               if ( !isset( $wgMediaHandlers[$type] ) ) {
+                       wfDebug( __METHOD__ . ": no handler found for $type.\n");
+                       return false;
+               }
+               $class = $wgMediaHandlers[$type];
+               if ( !isset( self::$handlers[$class] ) ) {
+                       self::$handlers[$class] = new $class;
+                       if ( !self::$handlers[$class]->isEnabled() ) {
+                               self::$handlers[$class] = false;
+                       }
+               }
+               return self::$handlers[$class];
+       }
+
+       /**
+        * Get an associative array mapping magic word IDs to parameter names.
+        * Will be used by the parser to identify parameters.
+        */
+       abstract function getParamMap();
+
+       /**
+        * Validate a thumbnail parameter at parse time.
+        * Return true to accept the parameter, and false to reject it.
+        * If you return false, the parser will do something quiet and forgiving.
+        *
+        * @param $name
+        * @param $value
+        */
+       abstract function validateParam( $name, $value );
+
+       /**
+        * Merge a parameter array into a string appropriate for inclusion in filenames
+        *
+        * @param $params array
+        */
+       abstract function makeParamString( $params );
+
+       /**
+        * Parse a param string made with makeParamString back into an array
+        *
+        * @param $str string
+        */
+       abstract function parseParamString( $str );
+
+       /**
+        * Changes the parameter array as necessary, ready for transformation.
+        * Should be idempotent.
+        * Returns false if the parameters are unacceptable and the transform should fail
+        * @param $image
+        * @param $params
+        */
+       abstract function normaliseParams( $image, &$params );
+
+       /**
+        * Get an image size array like that returned by getimagesize(), or false if it
+        * can't be determined.
+        *
+        * @param $image File: the image object, or false if there isn't one
+        * @param $path String: the filename
+        * @return Array Follow the format of PHP getimagesize() internal function. See http://www.php.net/getimagesize
+        */
+       abstract function getImageSize( $image, $path );
+
+       /**
+        * Get handler-specific metadata which will be saved in the img_metadata field.
+        *
+        * @param $image File: the image object, or false if there isn't one.
+        *   Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
+        * @param $path String: the filename
+        * @return String
+        */
+       function getMetadata( $image, $path ) { return ''; }
+
+       /**
+       * Get metadata version.
+       *
+       * This is not used for validating metadata, this is used for the api when returning
+       * metadata, since api content formats should stay the same over time, and so things
+       * using ForiegnApiRepo can keep backwards compatibility
+       *
+       * All core media handlers share a common version number, and extensions can
+       * use the GetMetadataVersion hook to append to the array (they should append a unique
+       * string so not to get confusing). If there was a media handler named 'foo' with metadata
+       * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
+       * version is 2, the end version string would look like '2;foo=3'.
+       *
+       * @return string version string
+       */
+       static function getMetadataVersion () {
+               $version = Array( '2' ); // core metadata version
+               wfRunHooks('GetMetadataVersion', Array(&$version));
+               return implode( ';', $version);
+        }
+
+       /**
+       * Convert metadata version.
+       *
+       * By default just returns $metadata, but can be used to allow
+       * media handlers to convert between metadata versions.
+       *
+       * @param $metadata Mixed String or Array metadata array (serialized if string)
+       * @param $version Integer target version
+       * @return Array serialized metadata in specified version, or $metadata on fail.
+       */
+       function convertMetadataVersion( $metadata, $version = 1 ) {
+               if ( !is_array( $metadata ) ) {
+
+                       //unserialize to keep return parameter consistent.
+                       wfSuppressWarnings();
+                       $ret = unserialize( $metadata );
+                       wfRestoreWarnings();
+                       return $ret;
+               }
+               return $metadata;
+       }
+
+       /**
+        * Get a string describing the type of metadata, for display purposes.
+        *
+        * @return string
+        */
+       function getMetadataType( $image ) { return false; }
+
+       /**
+        * Check if the metadata string is valid for this handler.
+        * If it returns MediaHandler::METADATA_BAD (or false), Image
+        * will reload the metadata from the file and update the database.
+        * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
+        * MediaHanlder::METADATA_COMPATIBLE if metadata is old but backwards
+        * compatible (which may or may not trigger a metadata reload).
+        * @return bool
+        */
+       function isMetadataValid( $image, $metadata ) {
+               return self::METADATA_GOOD;
+       }
+
+
+       /**
+        * Get a MediaTransformOutput object representing an alternate of the transformed
+        * output which will call an intermediary thumbnail assist script.
+        *
+        * Used when the repository has a thumbnailScriptUrl option configured.
+        *
+        * Return false to fall back to the regular getTransform().
+        * @return bool
+        */
+       function getScriptedTransform( $image, $script, $params ) {
+               return false;
+       }
+
+       /**
+        * Get a MediaTransformOutput object representing the transformed output. Does not
+        * actually do the transform.
+        *
+        * @param $image File: the image object
+        * @param $dstPath String: filesystem destination path
+        * @param $dstUrl String: Destination URL to use in output HTML
+        * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
+        * @return MediaTransformOutput
+        */
+       final function getTransform( $image, $dstPath, $dstUrl, $params ) {
+               return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+       }
+
+       /**
+        * Get a MediaTransformOutput object representing the transformed output. Does the
+        * transform unless $flags contains self::TRANSFORM_LATER.
+        *
+        * @param $image File: the image object
+        * @param $dstPath String: filesystem destination path
+        * @param $dstUrl String: destination URL to use in output HTML
+        * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
+        * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
+        *
+        * @return MediaTransformOutput
+        */
+       abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
+
+       /**
+        * Get the thumbnail extension and MIME type for a given source MIME type
+        * @return array thumbnail extension and MIME type
+        */
+       function getThumbType( $ext, $mime, $params = null ) {
+               $magic = MimeMagic::singleton();
+               if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
+                       // The extension is not valid for this mime type and we do
+                       // recognize the mime type
+                       $extensions = $magic->getExtensionsForType( $mime );
+                       if ( $extensions ) {
+                               return array( strtok( $extensions, ' ' ), $mime );
+                       }
+               }
+
+               // The extension is correct (true) or the mime type is unknown to
+               // MediaWiki (null)
+               return array( $ext, $mime );
+       }
+
+       /**
+        * True if the handled types can be transformed
+        * @return bool
+        */
+       function canRender( $file ) { return true; }
+       /**
+        * True if handled types cannot be displayed directly in a browser
+        * but can be rendered
+        * @return bool
+        */
+       function mustRender( $file ) { return false; }
+       /**
+        * True if the type has multi-page capabilities
+        * @return bool
+        */
+       function isMultiPage( $file ) { return false; }
+       /**
+        * Page count for a multi-page document, false if unsupported or unknown
+        * @return bool
+        */
+       function pageCount( $file ) { return false; }
+       /**
+        * The material is vectorized and thus scaling is lossless
+        * @return bool
+        */
+       function isVectorized( $file ) { return false; }
+       /**
+        * False if the handler is disabled for all files
+        * @return bool
+        */
+       function isEnabled() { return true; }
+
+       /**
+        * Get an associative array of page dimensions
+        * Currently "width" and "height" are understood, but this might be
+        * expanded in the future.
+        * Returns false if unknown or if the document is not multi-page.
+        *
+        * @param $image File
+        * @param $page Unused, left for backcompatibility?
+        * @return array
+        */
+       function getPageDimensions( $image, $page ) {
+               $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
+               return array(
+                       'width' => $gis[0],
+                       'height' => $gis[1]
+               );
+       }
+
+       /**
+        * Generic getter for text layer.
+        * Currently overloaded by PDF and DjVu handlers
+        * @return bool
+        */
+       function getPageText( $image, $page ) {
+               return false;
+       }
+
+       /**
+        * Get an array structure that looks like this:
+        *
+        * array(
+        *    'visible' => array(
+        *       'Human-readable name' => 'Human readable value',
+        *       ...
+        *    ),
+        *    'collapsed' => array(
+        *       'Human-readable name' => 'Human readable value',
+        *       ...
+        *    )
+        * )
+        * The UI will format this into a table where the visible fields are always
+        * visible, and the collapsed fields are optionally visible.
+        *
+        * The function should return false if there is no metadata to display.
+        */
+
+       /**
+        * @todo FIXME: I don't really like this interface, it's not very flexible
+        * I think the media handler should generate HTML instead. It can do
+        * all the formatting according to some standard. That makes it possible
+        * to do things like visual indication of grouped and chained streams
+        * in ogg container files.
+        * @return bool
+        */
+       function formatMetadata( $image ) {
+               return false;
+       }
+
+       /** sorts the visible/invisible field.
+        * Split off from ImageHandler::formatMetadata, as used by more than
+        * one type of handler.
+        *
+        * This is used by the media handlers that use the FormatMetadata class
+        *
+        * @param $metadataArray Array metadata array
+        * @return array for use displaying metadata.
+        */
+       function formatMetadataHelper( $metadataArray ) {
+                $result = array(
+                       'visible' => array(),
+                       'collapsed' => array()
+               );
+
+               $formatted = FormatMetadata::getFormattedData( $metadataArray );
+               // Sort fields into visible and collapsed
+               $visibleFields = $this->visibleMetadataFields();
+               foreach ( $formatted as $name => $value ) {
+                       $tag = strtolower( $name );
+                       self::addMeta( $result,
+                               in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
+                               'exif',
+                               $tag,
+                               $value
+                       );
+               }
+               return $result;
+       }
+
+       /**
+        * Get a list of metadata items which should be displayed when
+        * the metadata table is collapsed.
+        *
+        * @return array of strings
+        * @access protected
+        */
+       function visibleMetadataFields() {
+               $fields = array();
+               $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
+               foreach( $lines as $line ) {
+                       $matches = array();
+                       if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
+                               $fields[] = $matches[1];
+                       }
+               }
+               $fields = array_map( 'strtolower', $fields );
+               return $fields;
+       }
+
+
+       /**
+        * This is used to generate an array element for each metadata value
+        * That array is then used to generate the table of metadata values
+        * on the image page
+        *
+        * @param &$array Array An array containing elements for each type of visibility
+        * and each of those elements being an array of metadata items. This function adds
+        * a value to that array.
+        * @param $visibility string ('visible' or 'collapsed') if this value is hidden
+        * by default.
+        * @param $type String type of metadata tag (currently always 'exif')
+        * @param $id String the name of the metadata tag (like 'artist' for example).
+        * its name in the table displayed is the message "$type-$id" (Ex exif-artist ).
+        * @param $value String thingy goes into a wikitext table; it used to be escaped but
+        * that was incompatible with previous practise of customized display
+        * with wikitext formatting via messages such as 'exif-model-value'.
+        * So the escaping is taken back out, but generally this seems a confusing
+        * interface.
+        * @param $param String value to pass to the message for the name of the field
+        * as $1. Currently this parameter doesn't seem to ever be used.
+        *
+        * Note, everything here is passed through the parser later on (!)
+        */
+       protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
+               $msg = wfMessage( "$type-$id", $param );
+               if ( $msg->exists() ) {
+                       $name = $msg->text();
+               } else {
+                       // This is for future compatibility when using instant commons.
+                       // So as to not display as ugly a name if a new metadata
+                       // property is defined that we don't know about
+                       // (not a major issue since such a property would be collapsed
+                       // by default).
+                       wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id . "\n" );
+                       $name = wfEscapeWikiText( $id );
+               }
+               $array[$visibility][] = array(
+                       'id' => "$type-$id",
+                       'name' => $name,
+                       'value' => $value
+               );
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       function getShortDesc( $file ) {
+               global $wgLang;
+               return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       function getLongDesc( $file ) {
+               global $wgLang;
+               return wfMessage( 'file-info', htmlspecialchars( $wgLang->formatSize( $file->getSize() ) ),
+                       $file->getMimeType() )->parse();
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       static function getGeneralShortDesc( $file ) {
+               global $wgLang;
+               return $wgLang->formatSize( $file->getSize() );
+       }
+
+       /**
+        * @param $file File
+        * @return string
+        */
+       static function getGeneralLongDesc( $file ) {
+               global $wgLang;
+               return wfMessage( 'file-info', $wgLang->formatSize( $file->getSize() ),
+                       $file->getMimeType() )->parse();
+       }
+
+       /**
+        * Calculate the largest thumbnail width for a given original file size
+        * such that the thumbnail's height is at most $maxHeight.
+        * @param $boxWidth Integer Width of the thumbnail box.
+        * @param $boxHeight Integer Height of the thumbnail box.
+        * @param $maxHeight Integer Maximum height expected for the thumbnail.
+        * @return Integer.
+        */
+       public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
+               $idealWidth = $boxWidth * $maxHeight / $boxHeight;
+               $roundedUp = ceil( $idealWidth );
+               if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
+                       return floor( $idealWidth );
+               } else {
+                       return $roundedUp;
+               }
+       }
+
+       function getDimensionsString( $file ) {
+               return '';
+       }
+
+       /**
+        * Modify the parser object post-transform
+        */
+       function parserTransformHook( $parser, $file ) {}
+
+       /**
+        * File validation hook called on upload.
+        *
+        * If the file at the given local path is not valid, or its MIME type does not
+        * match the handler class, a Status object should be returned containing
+        * relevant errors.
+        *
+        * @param $fileName string The local path to the file.
+        * @return Status object
+        */
+       function verifyUpload( $fileName ) {
+               return Status::newGood();
+       }
+
+       /**
+        * Check for zero-sized thumbnails. These can be generated when
+        * no disk space is available or some other error occurs
+        *
+        * @param $dstPath string The location of the suspect file
+        * @param $retval int Return value of some shell process, file will be deleted if this is non-zero
+        * @return bool True if removed, false otherwise
+        */
+       function removeBadFile( $dstPath, $retval = 0 ) {
+               if( file_exists( $dstPath ) ) {
+                       $thumbstat = stat( $dstPath );
+                       if( $thumbstat['size'] == 0 || $retval != 0 ) {
+                               $result = unlink( $dstPath );
+
+                               if ( $result ) {
+                                       wfDebugLog( 'thumbnail',
+                                               sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
+                                                       $thumbstat['size'], $dstPath ) );
+                               } else {
+                                       wfDebugLog( 'thumbnail',
+                                               sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
+                                                       $thumbstat['size'], $dstPath ) );
+                               }
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Remove files from the purge list
+        *
+        * @param array $files
+        * @param array $options
+        */
+       public function filterThumbnailPurgeList( &$files, $options ) {
+               // Do nothing
+       }
+}
index eda57c0..ec67a39 100644 (file)
@@ -895,10 +895,7 @@ class MWMemcached {
        function _load_items( $sock, &$ret ) {
                while ( 1 ) {
                        $decl = fgets( $sock );
-                       if( $decl === false ) {
-                               $this->_debugprint( "Error reading socket for a memcached response\n" );
-                               return 0;
-                       } elseif ( $decl == "END\r\n" ) {
+                       if ( $decl == "END\r\n" ) {
                                return true;
                        } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match ) ) {
                                list( $rkey, $flags, $len ) = array( $match[1], $match[2], $match[3] );
@@ -942,12 +939,7 @@ class MWMemcached {
                                }
 
                        } else {
-                               $peer = $peerAddress = $peerPort = '';
-                               $gotPeer = socket_getpeername( $sock, $peerAddress, $peerPort );
-                               if( $gotPeer ) {
-                                       $peer = " from [$peerAddress:$peerPort";
-                               }
-                               $this->_debugprint( "Error parsing memcached response{$peer}\n" );
+                               $this->_debugprint( "Error parsing memcached response\n" );
                                return 0;
                        }
                }
index 912de41..322cb13 100644 (file)
@@ -565,29 +565,64 @@ class CoreParserFunctions {
        }
 
        /**
-        * Return the number of pages in the given category, or 0 if it's nonexis-
-        * tent.  This is an expensive parser function and can't be called too many
-        * times per page.
+        * Return the number of pages, files or subcats in the given category,
+        * or 0 if it's nonexistent. This is an expensive parser function and
+        * can't be called too many times per page.
         * @return string
         */
-       static function pagesincategory( $parser, $name = '', $raw = null ) {
+       static function pagesincategory( $parser, $name = '', $arg1 = null, $arg2 = null ) {
+               static $magicWords = null;
+               if ( is_null( $magicWords ) ) {
+                       $magicWords = new MagicWordArray( array(
+                               'pagesincategory_all',
+                               'pagesincategory_pages',
+                               'pagesincategory_subcats',
+                               'pagesincategory_files'
+                       ) );
+               }
                static $cache = array();
-               $category = Category::newFromName( $name );
 
-               if( !is_object( $category ) ) {
-                       $cache[$name] = 0;
+               // split the given option to its variable
+               if( self::isRaw( $arg1 ) ) {
+                       //{{pagesincategory:|raw[|type]}}
+                       $raw = $arg1;
+                       $type = $magicWords->matchStartToEnd( $arg2 );
+               } else {
+                       //{{pagesincategory:[|type[|raw]]}}
+                       $type = $magicWords->matchStartToEnd( $arg1 );
+                       $raw = $arg2;
+               }
+               if( !$type ) { //backward compatibility
+                       $type = 'pagesincategory_all';
+               }
+
+               $title = Title::makeTitleSafe( NS_CATEGORY, $name );
+               if( !$title ) { # invalid title
                        return self::formatRaw( 0, $raw );
                }
 
-               # Normalize name for cache
-               $name = $category->getName();
+               // Normalize name for cache
+               $name = $title->getDBkey();
 
-               $count = 0;
-               if( isset( $cache[$name] ) ) {
-                       $count = $cache[$name];
-               } elseif( $parser->incrementExpensiveFunctionCount() ) {
-                       $count = $cache[$name] = (int)$category->getPageCount();
+               if( !isset( $cache[$name] ) ) {
+                       $category = Category::newFromTitle( $title );
+
+                       $allCount = $subcatCount = $fileCount = $pagesCount = 0;
+                       if( $parser->incrementExpensiveFunctionCount() ) {
+                               // $allCount is the total number of cat members,
+                               // not the count of how many members are normal pages.
+                               $allCount = (int)$category->getPageCount();
+                               $subcatCount = (int)$category->getSubcatCount();
+                               $fileCount = (int)$category->getFileCount();
+                               $pagesCount = $allCount - $subcatCount - $fileCount;
+                       }
+                       $cache[$name]['pagesincategory_all'] = $allCount;
+                       $cache[$name]['pagesincategory_pages'] = $pagesCount;
+                       $cache[$name]['pagesincategory_subcats'] = $subcatCount;
+                       $cache[$name]['pagesincategory_files'] = $fileCount;
                }
+
+               $count = $cache[$name][$type];
                return self::formatRaw( $count, $raw );
        }
 
@@ -754,40 +789,34 @@ class CoreParserFunctions {
        }
 
        // Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}}
+       // or {{filepath|300px}}, {{filepath|200x300px}}, {{filepath|nowiki|200x300px}}, {{filepath|200x300px|nowiki}}
        public static function filepath( $parser, $name='', $argA='', $argB='' ) {
                $file = wfFindFile( $name );
-               $size = '';
-               $argA_int = intval( $argA );
-               $argB_int = intval( $argB );
-
-               if ( $argB_int > 0 ) {
-                       // {{filepath: | option | size }}
-                       $size = $argB_int;
-                       $option = $argA;
-
-               } elseif ( $argA_int > 0 ) {
-                       // {{filepath: | size [|option] }}
-                       $size = $argA_int;
-                       $option = $argB;
+               $isNowiki = false;
 
+               if( $argA == 'nowiki' ) {
+                       // {{filepath: | option [| size] }}
+                       $isNowiki = true;
+                       $parsedWidthParam = $parser->parseWidthParam( $argB );
                } else {
-                       // {{filepath: [|option] }}
-                       $option = $argA;
+                       // {{filepath: [| size [|option]] }}
+                       $parsedWidthParam = $parser->parseWidthParam( $argA );
+                       $isNowiki = ($argB == 'nowiki');
                }
 
                if ( $file ) {
                        $url = $file->getFullUrl();
 
                        // If a size is requested...
-                       if ( is_integer( $size ) ) {
-                               $mto = $file->transform( array( 'width' => $size ) );
+                       if ( count( $parsedWidthParam ) ) {
+                               $mto = $file->transform( $parsedWidthParam );
                                // ... and we can
                                if ( $mto && !$mto->isError() ) {
                                        // ... change the URL to point to a thumbnail.
                                        $url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
                                }
                        }
-                       if ( $option == 'nowiki' ) {
+                       if ( $isNowiki ) {
                                return array( $url, 'nowiki' => true );
                        }
                        return $url;
index 94af6a8..291db7a 100644 (file)
@@ -5042,27 +5042,22 @@ class Parser {
 
                                # Special case; width and height come in one variable together
                                if ( $type === 'handler' && $paramName === 'width' ) {
-                                       $m = array();
-                                       # (bug 13500) In both cases (width/height and width only),
-                                       # permit trailing "px" for backward compatibility.
-                                       if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
-                                               $width = intval( $m[1] );
-                                               $height = intval( $m[2] );
+                                       $parsedWidthParam = $this->parseWidthParam( $value );
+                                       if( isset( $parsedWidthParam['width'] ) ) {
+                                               $width = $parsedWidthParam['width'];
                                                if ( $handler->validateParam( 'width', $width ) ) {
                                                        $params[$type]['width'] = $width;
                                                        $validated = true;
                                                }
+                                       }
+                                       if( isset( $parsedWidthParam['height'] ) ) {
+                                               $height = $parsedWidthParam['height'];
                                                if ( $handler->validateParam( 'height', $height ) ) {
                                                        $params[$type]['height'] = $height;
                                                        $validated = true;
                                                }
-                                       } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
-                                               $width = intval( $value );
-                                               if ( $handler->validateParam( 'width', $width ) ) {
-                                                       $params[$type]['width'] = $width;
-                                                       $validated = true;
-                                               }
-                                       } # else no validation -- bug 13436
+                                       }
+                                       # else no validation -- bug 13436
                                } else {
                                        if ( $type === 'handler' ) {
                                                # Validate handler parameter
@@ -5781,4 +5776,32 @@ class Parser {
        function isValidHalfParsedText( $data ) {
                return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;
        }
+
+       /**
+        * Parsed a width param of imagelink like 300px or 200x300px
+        *
+        * @param $value String
+        *
+        * @return array
+        * @since 1.20
+        */
+       public function parseWidthParam( $value ) {
+               $parsedWidthParam = array();
+               if( $value === '' ) {
+                       return $parsedWidthParam;
+               }
+               $m = array();
+               # (bug 13500) In both cases (width/height and width only),
+               # permit trailing "px" for backward compatibility.
+               if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
+                       $width = intval( $m[1] );
+                       $height = intval( $m[2] );
+                       $parsedWidthParam['width'] = $width;
+                       $parsedWidthParam['height'] = $height;
+               } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
+                       $width = intval( $value );
+                       $parsedWidthParam['width'] = $width;
+               }
+               return $parsedWidthParam;
+       }
 }
index 3c43ed1..cd467a8 100644 (file)
@@ -300,6 +300,8 @@ class SpecialBlock extends FormSpecialPage {
         * @return String
         */
        protected function preText(){
+               $this->getOutput()->addModules( 'mediawiki.special.block' );
+
                $text = $this->msg( 'blockiptext' )->parse();
 
                $otherBlockMessages = array();
index 51c2d0f..a1cda0d 100644 (file)
@@ -166,6 +166,9 @@ class SpecialVersion extends SpecialPage {
                $svnInfo = self::getSvnInfo( $IP );
                if ( !$svnInfo && !$gitInfo ) {
                        $version = $wgVersion;
+               } elseif ( $gitInfo && $flags === 'nodb' ) {
+                       $shortSha1 = substr( $gitInfo, 0, 7 );
+                       $version = "$wgVersion ($shortSha1)";
                } elseif ( $gitInfo ) {
                        $shortSha1 = substr( $gitInfo, 0, 7 );
                        $shortSha1 = wfMessage( 'parentheses' )->params( $shortSha1 )->escaped();
index 41680d1..243f61b 100644 (file)
@@ -9,7 +9,6 @@
 乙太網      以太网
 點陣圖      位图
 常式 例程
-游標 光标
 光碟 光盘
 光碟機      光驱
 全形 全角
index 922b7de..9a9534f 100644 (file)
 細如髮
 繫於一髮
 膚髮
+皮膚
 生華髮
 蒼髮
 被髮佯狂
 棺材裡
 注釋
 月面
+路面
 修杰楷
 修杰麟
 學裡
index 8389281..6fd5e1b 100644 (file)
@@ -3917,6 +3917,7 @@ class Language {
         * @param $format Bool|Int true to process using language functions, or TS_ constant
         *     to return the expiry in a given timestamp
         * @return String
+        * @since 1.18
         */
        public function formatExpiry( $expiry, $format = true ) {
                static $infinity, $infinityMsg;
index 18d1dbc..455c64e 100644 (file)
  * @maintainers fdcn <fdcn64@gmail.com>, shinjiman <shinjiman@gmail.com>, PhiLiP <philip.npc@gmail.com>
  */
 class LanguageConverter {
+
+       /**
+        * languages supporting variants
+        * @since 1.20
+        * @var array
+        */
+       static public $languagesWithVariants = array(
+               'gan',
+               'iu',
+               'kk',
+               'ku',
+               'shi',
+               'sr',
+               'tg',
+               'zh',
+       );
+
        var $mMainLanguageCode;
        var $mVariants, $mVariantFallbacks, $mVariantNames;
        var $mTablesLoaded = false;
index 48ec9b9..461db78 100644 (file)
@@ -356,6 +356,10 @@ $magicWords = array(
        'url_query'              => array( 0,    'QUERY' ),
        'defaultsort_noerror'    => array( 0,    'noerror' ),
        'defaultsort_noreplace'  => array( 0,    'noreplace' ),
+       'pagesincategory_all'    => array( 0,    'all' ),
+       'pagesincategory_pages'  => array( 0,    'pages' ),
+       'pagesincategory_subcats' => array( 0,   'subcats' ),
+       'pagesincategory_files'  => array( 0,    'files' ),
 );
 
 /**
index fba4e9f..eeafab8 100644 (file)
@@ -9,5 +9,7 @@
  *
  */
 
+$rtl = true;
+
 # Inherit everything for now
 $fallback = 'kk-arab, kk-cyrl';
index 78e9481..a17741f 100644 (file)
@@ -13,6 +13,8 @@
  * @author Marmzok
  */
 
+$rtl = true;
+
 $fallback = 'ckb';
 
 $digitTransformTable = array(
index 3846ef5..ec395eb 100644 (file)
@@ -171,10 +171,6 @@ class TextPassDumper extends BackupDumper {
                $input = fopen( $this->input, "rt" );
                $result = $this->readDump( $input );
 
-               if ( WikiError::isError( $result ) ) {
-                       throw new MWException( $result->getMessage() );
-               }
-
                if ( $this->spawnProc ) {
                        $this->closeSpawn();
                }
@@ -328,6 +324,10 @@ class TextPassDumper extends BackupDumper {
                }
        }
 
+       /**
+        * @throws MWException Failure to parse XML input
+        * @return true
+        */
        function readDump( $input ) {
                $this->buffer = "";
                $this->openElement = false;
@@ -352,7 +352,18 @@ class TextPassDumper extends BackupDumper {
                        $chunk = fread( $input, $bufferSize );
                        if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) {
                                wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" );
-                               return new WikiXmlError( $parser, 'XML import parse failure', $chunk, $offset );
+
+                               $byte = xml_get_current_byte_index( $parser );
+                               $msg = wfMsgHtml( 'xml-error-string',
+                                       'XML import parse failure',
+                                       xml_get_current_line_number( $parser ),
+                                       xml_get_current_column_number( $parser ),
+                                       $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte -$offset, 16 ) . '"' ) ),
+                                       xml_error_string( xml_get_error_code( $parser ) ) );
+
+                               xml_parser_free( $parser );
+
+                               throw new MWException( $msg );
                        }
                        $offset += strlen( $chunk );
                } while ( $chunk !== false && !feof( $input ) );
index 2a99fa1..6860a5a 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Deletes a batch of pages
+ * Deletes a batch of pages.
  * Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
  * where
  *     [listfile] is a file where each line contains the title of a page to be
index 211dc4e..13b3c49 100644 (file)
@@ -17,6 +17,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
index de6f652..126eed2 100644 (file)
@@ -17,6 +17,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
index 7e83d5f..a81e3d9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Creates a sitemap for the site
+ * Creates a sitemap for the site.
  *
  * Copyright © 2005, Ævar Arnfjörð Bjarmason, Jens Frank <jeluf@gmx.de> and
  * Brion Vibber <brion@pobox.com>
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that generates a sitemap for the site.
+ *
+ * @ingroup Maintenance
+ */
 class GenerateSitemap extends Maintenance {
        const GS_MAIN = -2;
        const GS_TALK = -1;
index 0322fa2..c47c61e 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 /**
+ * Display replication lag times.
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that displays replication lag times.
+ *
+ * @ingroup Maintenance
+ */
 class GetLagTimes extends Maintenance {
        public function __construct() {
                parent::__construct();
index 3d13bc4..0270052 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * This script reports the hostname of a slave server.
+ * Reports the hostname of a slave server.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that reports the hostname of a slave server.
+ *
+ * @ingroup Maintenance
+ */
 class GetSlaveServer extends Maintenance {
        public function __construct() {
                parent::__construct();
index eb04411..34558b6 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 /**
- * Outputs page text to stdout, useful for command-line editing automation.
+ * Outputs page text to stdout.
+ * Useful for command-line editing automation.
  * Example: php getText.php "page title" | sed -e '...' | php edit.php "page title"
  *
  * This program is free software; you can redistribute it and/or modify
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that outputs page text to stdout.
+ *
+ * @ingroup Maintenance
+ */
 class GetTextMaint extends Maintenance {
        public function __construct() {
                parent::__construct();
index 2ad0872..b6e0fe0 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 /**
- * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
+ * Import XML dump files into the current wiki.
+ *
+ * Copyright © 2005 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,6 +27,8 @@
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
 /**
+ * Maintenance script that imports XML dump files into the current wiki.
+ *
  * @ingroup Maintenance
  */
 class BackupReader extends Maintenance {
index 5eb68f2..ac5d144 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Support functions for the importImages script
+ * Support functions for the importImages.php script
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index bd077ff..76ec565 100644 (file)
@@ -1,8 +1,7 @@
 <?php
-
 /**
- * Maintenance script to import one or more images from the local file system into
- * the wiki without using the web-based interface.
+ * Import one or more images from the local file system into the wiki without
+ * using the web-based interface.
  *
  * "Smart import" additions:
  * - aim: preserve the essential metadata (user, description) when importing medias from an existing wiki
index 0ce9388..65ac65a 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 /**
- * Maintenance script to import all scripts in the MediaWiki namespace from a
- * local site.
+ * Import all scripts in the MediaWiki namespace from a local site.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to import all scripts in the MediaWiki namespace from a
+ * local site.
+ *
+ * @ingroup Maintenance
+ */
 class ImportSiteScripts extends Maintenance {
        public function __construct() {
                parent::__construct();
@@ -32,17 +37,17 @@ class ImportSiteScripts extends Maintenance {
                $this->addArg( 'index', 'index.php base url' );
                $this->addOption( 'username', 'User name of the script importer' );
        }
-       
+
        public function execute() {
                global $wgUser;
 
                $user = User::newFromName( $this->getOption( 'username', 'ScriptImporter' ) );
                $wgUser = $user;
-               
+
                $baseUrl = $this->getArg( 1 );
                $pageList = $this->fetchScriptList();
                $this->output( 'Importing ' . count( $pageList ) . " pages\n" );
-               
+
                foreach ( $pageList as $page ) {
                        $title = Title::makeTitleSafe( NS_MEDIAWIKI, $page );
                        if ( !$title ) {
@@ -51,34 +56,34 @@ class ImportSiteScripts extends Maintenance {
                        }
 
                        $this->output( "Importing $page\n" );
-                       $url = wfAppendQuery( $baseUrl, array( 
-                               'action' => 'raw', 
+                       $url = wfAppendQuery( $baseUrl, array(
+                               'action' => 'raw',
                                'title' => "MediaWiki:{$page}" ) );
                        $text = Http::get( $url );
 
                        $wikiPage = WikiPage::factory( $title );
                        $wikiPage->doEdit( $text, "Importing from $url", 0, false, $user );
                }
-               
+
        }
-       
+
        protected function fetchScriptList() {
-               $data = array( 
+               $data = array(
                        'action' => 'query',
                        'format' => 'php',//'json',
                        'list' => 'allpages',
                        'apnamespace' => '8',
-                       'aplimit' => '500', 
+                       'aplimit' => '500',
                );
                $baseUrl = $this->getArg( 0 );
                $pages = array();
-               
+
                do {
                        $url = wfAppendQuery( $baseUrl, $data );
                        $strResult = Http::get( $url );
                        //$result = FormatJson::decode( $strResult ); // Still broken
                        $result = unserialize( $strResult );
-                       
+
                        if ( !empty( $result['query']['allpages'] ) ) {
                                foreach ( $result['query']['allpages'] as $page ) {
                                        if ( substr( $page['title'], -3 ) === '.js' ) {
@@ -92,9 +97,9 @@ class ImportSiteScripts extends Maintenance {
                                $this->output( "Fetching new batch from {$data['apfrom']}\n" );
                        }
                } while ( isset( $result['query-continue'] ) );
-               
+
                return $pages;
-               
+
        }
 }
 
index ec9ff00..5623fb0 100644 (file)
@@ -1,8 +1,6 @@
 <?php
-
 /**
- * Maintenance script allows creating or editing pages using
- * the contents of a text file
+ * Create or edit pages using the contents of a text file.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index eab9c8d..35918bb 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Maintenance script to re-initialise or update the site statistics table
+ * Re-initialise or update the site statistics table.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to re-initialise or update the site statistics table
+ *
+ * @ingroup Maintenance
+ */
 class InitStats extends Maintenance {
        public function __construct() {
                parent::__construct();
index 8e0b2e1..6339773 100644 (file)
@@ -1,6 +1,7 @@
 <?php
-
 /**
+ * CLI-based MediaWiki installation and configuration.
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -16,8 +17,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
- * @see wfWaitForSlaves()
  */
 
 if ( !function_exists( 'version_compare' ) || ( version_compare( phpversion(), '5.3.2' ) < 0 ) ) {
@@ -31,6 +32,11 @@ define( 'MEDIAWIKI_INSTALL', true );
 
 require_once( dirname( dirname( __FILE__ ) )."/maintenance/Maintenance.php" );
 
+/**
+ * Maintenance script to install and configure MediaWiki
+ *
+ * @ingroup Maintenance
+ */
 class CommandLineInstaller extends Maintenance {
        function __construct() {
                parent::__construct();
index 2c38ed9..c0a4dba 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Maintenance script to do test JavaScript validity parses using jsmin+'s parser
+ * Test JavaScript validity parses using jsmin+'s parser
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to do test JavaScript validity parses using jsmin+'s parser
+ *
+ * @ingroup Maintenance
+ */
 class JSParseHelper extends Maintenance {
        var $errs = 0;
 
index dc8bff5..19c549a 100644 (file)
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to show database lag.
+ *
+ * @ingroup Maintenance
+ */
 class DatabaseLag extends Maintenance {
        public function __construct() {
                parent::__construct();
index 088eaa3..ed8250b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /**
- * This script makes several 'set', 'incr' and 'get' requests on every
- * memcached server and shows a report.
+ * Makes several 'set', 'incr' and 'get' requests on every memcached
+ * server and shows a report.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that  makes several 'set', 'incr' and 'get' requests
+ * on every memcached server and shows a report.
+ *
+ * @ingroup Maintenance
+ */
 class mcTest extends Maintenance {
        public function __construct() {
                parent::__construct();
index 6a9baa8..8107016 100644 (file)
@@ -28,6 +28,13 @@ define( 'MW_NO_EXTENSION_MESSAGES', 1 );
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 $maintClass = 'MergeMessageFileList';
 $mmfl = false;
+
+/**
+ * Maintenance script that merges $wgExtensionMessagesFiles from various
+ * extensions to produce a single array containing all message files.
+ *
+ * @ingroup Maintenance
+ */
 class MergeMessageFileList extends Maintenance {
 
        function __construct() {
index 297aaf3..451b598 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that re-assigns users from an old group to a new one.
+ *
+ * @ingroup Maintenance
+ */
 class MigrateUserGroup extends Maintenance {
        public function __construct() {
                parent::__construct();
index e1fd862..b17d8fe 100644 (file)
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that minifies a file or set of files.
+ *
+ * @ingroup Maintenance
+ */
 class MinifyScript extends Maintenance {
        var $outDir;
 
index a7739c2..f846994 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Maintenance script to move a batch of pages
+ * Move a batch of pages.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  * @author Tim Starling
  *
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script to move a batch of pages.
+ *
+ * @ingroup Maintenance
+ */
 class MoveBatch extends Maintenance {
        public function __construct() {
                parent::__construct();
index 74bd657..e2de686 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Check for articles to fix after adding/deleting namespaces
  *
- * Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
+ * Copyright © 2005-2007 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that checks for articles to fix after
+ * adding/deleting namespaces.
+ *
+ * @ingroup Maintenance
+ */
 class NamespaceConflictChecker extends Maintenance {
 
        /**
index ac4e723..bee4065 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @todo Make this work on PostgreSQL and maybe other database servers
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that picks a database that has pending jobs.
+ *
+ * @ingroup Maintenance
+ */
 class nextJobDB extends Maintenance {
        public function __construct() {
                parent::__construct();
index 5d4f374..1defe1b 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * Remove pages with only 1 revision from the MediaWiki namespace, without
  * flooding recent changes, delete logs, etc.
@@ -28,6 +27,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  * @author Steve Sanbeg
  * based on nukePage by Rob Church
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that removes pages with only one revision from the
+ * MediaWiki namespace.
+ *
+ * @ingroup Maintenance
+ */
 class NukeNS extends Maintenance {
        public function __construct() {
                parent::__construct();
index f63de43..3193d43 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @ingroup Maintenance
  * @author Rob Church <robchur@gmail.com>
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that erases a page record from the database.
+ *
+ * @ingroup Maintenance
+ */
 class NukePage extends Maintenance {
        public function __construct() {
                parent::__construct();
index faaadd3..1ab3b99 100644 (file)
@@ -1,11 +1,11 @@
 <?php
 /**
- * Look for 'orphan' revisions hooked to pages which don't exist
- * And 'childless' pages with no revisions.
+ * Look for 'orphan' revisions hooked to pages which don't exist and
+ * 'childless' pages with no revisions.
  * Then, kill the poor widows and orphans.
  * Man this is depressing.
  *
- * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
+ * Copyright © 2005 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
+ * @file
  * @author <brion@pobox.com>
  * @ingroup Maintenance
  */
 
 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
 
+/**
+ * Maintenance script that looks for 'orphan' revisions hooked to pages which
+ * don't exist and 'childless' pages with no revisions.
+ *
+ * @ingroup Maintenance
+ */
 class Orphans extends Maintenance {
        public function __construct() {
                parent::__construct();
                $this->mDescription = "Look for 'orphan' revisions hooked to pages which don't exist\n" .
-                                                               "And 'childless' pages with no revisions\n" .
+                                                               "and 'childless' pages with no revisions\n" .
                                                                "Then, kill the poor widows and orphans\n" .
                                                                "Man this is depressing";
                $this->addOption( 'fix', 'Actually fix broken entries' );
index c4ba66e..2dcf757 100644 (file)
@@ -129,7 +129,7 @@ class SyncFileBackend extends Maintenance {
                        if ( $status->isOK() ) {
                                $lastOKPos = max( $lastOKPos, $lastPosInBatch );
                        } else {
-                               $this->output( print_r( $status->getErrorsArray(), true ) );
+                               $this->error( print_r( $status->getErrorsArray(), true ) );
                                break; // no gaps; everything up to $lastPos must be OK
                        }
 
index 0e85050..9900ab1 100644 (file)
@@ -632,6 +632,7 @@ return array(
                        'jquery.textSelection',
                        'jquery.byteLimit',
                ),
+               'position' => 'top',
        ),
        'mediawiki.action.history' => array(
                'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js',
index 14b845d..bd07cd0 100644 (file)
@@ -1,42 +1,74 @@
-( function ( $, mw ) {
-       var isReady, toolbar, currentFocused;
+( function ( mw, $ ) {
+       var isReady, toolbar, currentFocused, queue, $toolbar, slice;
 
        isReady = false;
+       queue = [];
+       $toolbar = false;
+       slice = Array.prototype.slice;
+
+       /**
+        * Internal helper that does the actual insertion
+        * of the button into the toolbar.
+        * See mw.toolbar.addButton for parameter documentation.
+        */
+       function insertButton( b /* imageFile */, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
+               // Backwards compatibility
+               if ( typeof b !== 'object' ) {
+                       b = {
+                               imageFile: b,
+                               speedTip: speedTip,
+                               tagOpen: tagOpen,
+                               tagClose: tagClose,
+                               sampleText: sampleText,
+                               imageId: imageId,
+                               selectText: selectText
+                       };
+               }
+               var $image = $('<img>', {
+                       width : 23,
+                       height: 22,
+                       src   : b.imageFile,
+                       alt   : b.speedTip,
+                       title : b.speedTip,
+                       id    : b.imageId || undefined,
+                       'class': 'mw-toolbar-editbutton'
+               } ).click( function () {
+                       toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText, b.selectText );
+                       return false;
+               } );
+
+               $toolbar.append( $image );
+               return true;
+       }
 
        toolbar = {
-               $toolbar: false,
-               buttons: [],
                /**
-                * If you want to add buttons, use
-                * mw.toolbar.addButton( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText );
+                * Add buttons to the toolbar.
+                * Takes care of race conditions and time-based dependencies
+                * by placing buttons in a queue if this method is called before
+                * the toolbar is created.
+                * @param {Object} button: Object with the following properties:
+                * - imageFile
+                * - speedTip
+                * - tagOpen
+                * - tagClose
+                * - sampleText
+                * - imageId
+                * - selectText
+                * For compatiblity, passing the above as separate arguments
+                * (in the listed order) is also supported.
                 */
                addButton: function () {
                        if ( isReady ) {
-                               toolbar.insertButton.apply( toolbar, arguments );
+                               insertButton.apply( toolbar, arguments );
                        } else {
-                               toolbar.buttons.push( [].slice.call( arguments ) );
-                       }       
-               },
-               insertButton: function ( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
-                       var image = $('<img>', {
-                               width : 23,
-                               height: 22,
-                               src   : imageFile,
-                               alt   : speedTip,
-                               title : speedTip,
-                               id    : imageId || '',
-                               'class': 'mw-toolbar-editbutton'
-                       } ).click( function () {
-                               mw.toolbar.insertTags( tagOpen, tagClose, sampleText, selectText );
-                               return false;
-                       } );
-
-                       toolbar.$toolbar.append( image );
-                       return true;
+                               // Convert arguments list to array
+                               queue.push( slice.call( arguments ) );
+                       }
                },
 
                /**
-                * apply tagOpen/tagClose to selection in textarea,
+                * Apply tagOpen/tagClose to selection in textarea,
                 * use sampleText instead of selection if there is none.
                 */
                insertTags: function ( tagOpen, tagClose, sampleText, selectText ) {
@@ -51,7 +83,8 @@
                        }
                },
 
-               // For backwards compatibility
+               // For backwards compatibility,
+               // Called from EditPage.php, maybe in other places as well.
                init: function () {}
        };
 
        window.addButton = toolbar.addButton;
        window.insertTags = toolbar.insertTags;
 
-       // Explose publicly
+       // Explose API publicly
        mw.toolbar = toolbar;
 
        $( document ).ready( function () {
-               var buttons, i, c, iframe;
+               var buttons, i, b, iframe;
 
                // currentFocus is used to determine where to insert tags
                currentFocused = $( '#wpTextbox1' );
 
-               // Populate the selector cache for $toolbar 
-               toolbar.$toolbar = $( '#toolbar' );
+               // Populate the selector cache for $toolbar
+               $toolbar = $( '#toolbar' );
 
                // Legacy: Merge buttons from mwCustomEditButtons
-               buttons = [].concat( toolbar.buttons, window.mwCustomEditButtons );
+               buttons = [].concat( queue, window.mwCustomEditButtons );
+               // Clear queue
+               queue.length = 0;
                for ( i = 0; i < buttons.length; i++ ) {
-                       if ( $.isArray( buttons[i] ) ) {
-                               // Passes our button array as arguments
-                               toolbar.insertButton.apply( toolbar, buttons[i] );
+                       b = buttons[i];
+                       if ( $.isArray( b ) ) {
+                               // Forwarded arguments array from mw.toolbar.addButton
+                               insertButton.apply( toolbar, b );
                        } else {
-                               // Legacy mwCustomEditButtons is an object
-                               c = buttons[i];
-                               toolbar.insertButton( c.imageFile, c.speedTip, c.tagOpen, 
-                                       c.tagClose, c.sampleText, c.imageId, c.selectText );
+                               // Raw object from legacy mwCustomEditButtons
+                               insertButton( b );
                        }
                }
 
                }
        });
 
-}( jQuery, mediaWiki ) );
+}( mediaWiki, jQuery ) );
index b871ac7..95e5e80 100644 (file)
                 * @constructor
                 * @param {Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone).
                 * Object must have non-blank 'protocol', 'host', and 'path' properties.
+                * This parameter is optional. If omitted (or set to undefined, null or empty string), then an object will be created
+                * for the default uri of this constructor (e.g. document.location for mw.Uri in MediaWiki core).
                 * @param {Object|Boolean} Object with options, or (backwards compatibility) a boolean for strictMode
                 * - strictMode {Boolean} Trigger strict mode parsing of the url. Default: false
                 * - overrideKeys {Boolean} Wether to let duplicate query parameters override eachother (true) or automagically
                                overrideKeys: false
                        }, options );
 
-                       if ( uri !== undefined && uri !== null || uri !== '' ) {
+                       if ( uri !== undefined && uri !== null && uri !== '' ) {
                                if ( typeof uri === 'string' ) {
                                        this.parse( uri, options );
                                } else if ( typeof uri === 'object' ) {
                                                this.query = {};
                                        }
                                }
+                       } else {
+                               // If we didn't get a URI in the constructor, use the default one.
+                               return defaultUri.clone();
                        }
 
                        // protocol-relative URLs
index deb7795..21cb7f3 100644 (file)
@@ -880,15 +880,6 @@ var mw = ( function ( $, undefined ) {
                                // Allow calling by single module name
                                if ( typeof dependencies === 'string' ) {
                                        dependencies = [dependencies];
-                                       if ( registry[dependencies[0]] !== undefined ) {
-                                               // Cache repetitively accessed deep level object member
-                                               regItemDeps = registry[dependencies[0]].dependencies;
-                                               // Cache to avoid looped access to length property
-                                               regItemDepLen = regItemDeps.length;
-                                               for ( n = 0; n < regItemDepLen; n += 1 ) {
-                                                       dependencies[dependencies.length] = regItemDeps[n];
-                                               }
-                                       }
                                }
 
                                // Add ready and error callbacks if they were given
@@ -1334,7 +1325,7 @@ var mw = ( function ( $, undefined ) {
                                                }
                                        }
 
-                                       if (filtered.length === 0) {
+                                       if ( filtered.length === 0 ) {
                                                return;
                                        }
                                        // Resolve entire dependency map
index 903a4f7..57f81a0 100644 (file)
@@ -198,12 +198,22 @@ pre, .mw-code {
        border: 1px dashed #2f6fab;
        color: black;
        background-color: #f9f9f9;
-       /* Handle overflow (bug 260) */
-       overflow: auto;
-       /* IE 7 is the first IE to support overflow but got it wrong */
-       /* IE 8+ is fine */
-       *padding-bottom: 21px;
-       *overflow-y: hidden;
+
+       /*
+        * Wrap properly.
+        * - pre-wrap: causes the browser to naturally wrap by displaying
+        *   words on the next line if they don't fit on the same line
+        *   within the box (does not cut off words).
+        * - break-word: forces the browser to wrap anywhere (even within
+        *   a word) if it is (still) too long for the line.
+        * When only using break-word in a <pre>, the browser only uses
+        * the force behavior and as a result almost always cuts half-way
+        * a word. When only using pre-wrap, too-long words will still
+        * cause the page layout to break. The combination is magic :).
+        * See also https://bugzilla.wikimedia.org/show_bug.cgi?id=260#c20
+        */
+       white-space: pre-wrap;
+       word-wrap: break-word;
 }
 
 /* Tables */
index 1b839b5..8c114f0 100644 (file)
@@ -828,12 +828,13 @@ h1:lang(hi),
 h1:lang(kn),
 h1:lang(ml),
 h1:lang(mr),
+h1:lang(my),
 h1:lang(or),
 h1:lang(pa),
 h1:lang(sa),
 h1:lang(ta),
 h1:lang(te) {
-       line-height: 1.5em !important;
+       line-height: 1.6em !important;
 }
 h2:lang(as), h3:lang(as), h4:lang(as), h5:lang(as), h6:lang(as),
 h2:lang(bho), h3:lang(bho), h4:lang(bho), h5:lang(bho), h6:lang(bho),
@@ -844,6 +845,7 @@ h2:lang(hi), h3:lang(hi), h4:lang(hi), h5:lang(hi), h6:lang(hi),
 h2:lang(kn), h3:lang(kn), h4:lang(kn), h5:lang(kn), h6:lang(kn),
 h2:lang(ml), h3:lang(ml), h4:lang(ml), h5:lang(ml), h6:lang(ml),
 h2:lang(mr), h3:lang(mr), h4:lang(mr), h5:lang(mr), h6:lang(mr),
+h2:lang(my), h3:lang(my), h4:lang(my), h5:lang(my), h6:lang(my),
 h2:lang(or), h3:lang(or), h4:lang(or), h5:lang(or), h6:lang(or),
 h2:lang(pa), h3:lang(pa), h4:lang(pa), h5:lang(pa), h6:lang(pa),
 h2:lang(sa), h3:lang(sa), h4:lang(sa), h5:lang(sa), h6:lang(sa),
index 9913fb9..9ab5c78 100644 (file)
@@ -344,27 +344,7 @@ test( 'Handle protocol-relative URLs', function () {
 
 test( 'Bad calls', function () {
        var uri;
-       expect( 5 );
-
-       raises(
-               function () {
-                       new mw.Uri();
-               },
-               function ( e ) {
-                       return e.message === 'Bad constructor arguments';
-               },
-               'throw error on no arguments to constructor'
-       );
-
-       raises(
-               function () {
-                       new mw.Uri( '' );
-               },
-               function ( e ) {
-                       return e.message === 'Bad constructor arguments';
-               },
-               'throw error on empty string as argument to constructor'
-       );
+       expect( 3 );
 
        raises(
                function () {
@@ -415,3 +395,23 @@ test( 'bug 35658', function () {
        equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
 
 } );
+
+QUnit.test( 'Constructor falls back to default location', function (assert) {
+       var testuri, MyUri, uri;
+       QUnit.expect( 4 );
+
+       testuri = 'http://example.org/w/index.php';
+       MyUri = mw.UriRelative( testuri );
+
+       uri = new MyUri();
+       assert.equal( uri.toString(), testuri, 'no arguments' );
+
+       uri = new MyUri( undefined );
+       assert.equal( uri.toString(), testuri, 'undefined' );
+
+       uri = new MyUri( null );
+       assert.equal( uri.toString(), testuri, 'null' );
+
+       uri = new MyUri( '' );
+       assert.equal( uri.toString(), testuri, 'empty string' );
+} );