Merge "Darker labels in new login and create acct forms"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 30 May 2013 20:39:14 +0000 (20:39 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 30 May 2013 20:39:14 +0000 (20:39 +0000)
35 files changed:
RELEASE-NOTES-1.22
includes/DefaultSettings.php
includes/HTMLForm.php
includes/cache/MessageCache.php
includes/clientpool/RedisConnectionPool.php
includes/debug/Debug.php
includes/logging/PatrolLog.php
includes/specials/SpecialPasswordReset.php
includes/templates/UsercreateVForm.php
languages/Language.php
languages/messages/MessagesEn.php
languages/messages/MessagesGu.php
languages/messages/MessagesQqq.php
maintenance/Doxyfile
maintenance/language/messages.inc
maintenance/mwdocgen.php
resources/mediawiki.special/images/glyph-people-large.png
resources/mediawiki.special/images/icon-contributors.png
resources/mediawiki.special/images/icon-edits.png
resources/mediawiki.special/images/icon-lock.png
resources/mediawiki.special/images/icon-pages.png
resources/mediawiki.special/mediawiki.special.createAccount.vform.css
resources/mediawiki/mediawiki.htmlform.js
skins/common/images/question-small.png [new file with mode: 0644]
skins/common/images/question.svg [new file with mode: 0644]
skins/common/shared.css
tests/phpunit/AutoLoaderTest.php [deleted file]
tests/phpunit/StructureTest.php [deleted file]
tests/phpunit/includes/upload/UploadBaseTest.php [new file with mode: 0644]
tests/phpunit/includes/upload/UploadTest.php [deleted file]
tests/phpunit/resources/ResourcesTest.php [deleted file]
tests/phpunit/structure/AutoLoaderTest.php [new file with mode: 0644]
tests/phpunit/structure/ResourcesTest.php [new file with mode: 0644]
tests/phpunit/structure/StructureTest.php [new file with mode: 0644]
tests/phpunit/suite.xml

index ded2cee..1591fa9 100644 (file)
@@ -27,6 +27,8 @@ production.
 * $wgJsMimeType is no longer used by core. Most usage has been removed since
   HTML output is now exclusively HTML5.
 * $wgDBOracleDRCP added. True enables persistent connection with DRCP on Oracle.
+* $wgLogAutopatrol added to allow disabling logging of autopatrol edits in the logging table.
+  default for $wgLogAutopatrol is true.
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements and attributes.
index 1b56547..b86de8c 100644 (file)
@@ -5147,6 +5147,9 @@ $wgUseRCPatrol = true;
 /** Use new page patrolling to check new pages on Special:Newpages */
 $wgUseNPPatrol = true;
 
+/** Log autopatrol actions to the log table */
+$wgLogAutopatrol = true;
+
 /** Provide syndication feeds (RSS, Atom) for, e.g., Recentchanges, Newpages */
 $wgFeed = true;
 
index ad7574f..e4a77a0 100644 (file)
@@ -1862,12 +1862,18 @@ class HTMLCheckField extends HTMLFormField {
  * are of the form "columnName-rowName"
  *
  * Options:
- *   columns:           Required list of columns in the matrix.
- *   rows:              Required list of rows in the matrix.
- *   force-options-on:  Accepts array of column-row tags to be displayed as enabled
- *                      but unavailable to change
- *   force-options-off: Accepts array of column-row tags to be displayed as disabled
- *                      but unavailable to change.
+ *   - columns
+ *     - Required list of columns in the matrix.
+ *   - rows
+ *     - Required list of rows in the matrix.
+ *   - force-options-on
+ *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+ *   - force-options-off
+ *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+ *   - tooltips
+ *     - Optional array mapping row label to tooltip content
+ *   - tooltip-class
+ *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
  */
 class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
 
@@ -1944,8 +1950,21 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
                }
                $tableContents .= Html::rawElement( 'tr', array(), "\n$headerContents\n" );
 
+               $tooltipClass = 'mw-icon-question';
+               if ( isset( $this->mParams['tooltip-class'] ) ) {
+                       $tooltipClass = $this->mParams['tooltip-class'];
+               }
+
                // Build the options matrix
                foreach ( $rows as $rowLabel => $rowTag ) {
+                       // Append tooltip if configured
+                       if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) {
+                               $tooltipAttribs = array(
+                                       'class' => "mw-htmlform-tooltip $tooltipClass",
+                                       'title' =>  $this->mParams['tooltips'][$rowLabel],
+                               );
+                               $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' );
+                       }
                        $rowContents = Html::rawElement( 'td', array(), $rowLabel );
                        foreach ( $columns as $columnTag ) {
                                $thisTag = "$columnTag-$rowTag";
index 307d301..7dc8d05 100644 (file)
  */
 
 /**
- *
+ * MediaWiki message cache structure version.
+ * Bump this whenever the message cache format has changed.
+ */
+define( 'MSG_CACHE_VERSION', 1 );
+
+/**
+ * Memcached timeout when loading a key.
+ * See MessageCache::load()
  */
 define( 'MSG_LOAD_TIMEOUT', 60 );
+
+/**
+ * Memcached timeout when locking a key for a writing operation.
+ * See MessageCache::lock()
+ */
 define( 'MSG_LOCK_TIMEOUT', 30 );
+/**
+ * Number of times we will try to acquire a lock from Memcached.
+ * This comes in addition to MSG_LOCK_TIMEOUT.
+ */
 define( 'MSG_WAIT_TIMEOUT', 30 );
-define( 'MSG_CACHE_VERSION', 1 );
 
 /**
  * Message cache
@@ -44,10 +59,16 @@ class MessageCache {
         */
        protected $mCache;
 
-       // Should  mean that database cannot be used, but check
+       /**
+        * Should  mean that database cannot be used, but check
+        * @var bool $mDisable
+        */
        protected $mDisable;
 
-       /// Lifetime for cache, used by object caching
+       /**
+        * Lifetime for cache, used by object caching.
+        * Set on construction, see __construct().
+        */
        protected $mExpiry;
 
        /**
@@ -56,18 +77,21 @@ class MessageCache {
         */
        protected $mParserOptions, $mParser;
 
-       /// Variable for tracking which variables are already loaded
+       /**
+        * Variable for tracking which variables are already loaded
+        * @var array $mLoadedLanguages
+        */
        protected $mLoadedLanguages = array();
 
        /**
         * Singleton instance
         *
-        * @var MessageCache
+        * @var MessageCache $instance
         */
        private static $instance;
 
        /**
-        * @var bool
+        * @var bool $mInParser
         */
        protected $mInParser = false;
 
@@ -98,6 +122,11 @@ class MessageCache {
                self::$instance = null;
        }
 
+       /**
+        * @param ObjectCache $memCached A cache instance. If none, fall back to CACHE_NONE.
+        * @param bool $useDB
+        * @param int $expiry Lifetime for cache. @see $mExpiry.
+        */
        function __construct( $memCached, $useDB, $expiry ) {
                if ( !$memCached ) {
                        $memCached = wfGetCache( CACHE_NONE );
@@ -592,7 +621,10 @@ class MessageCache {
        }
 
        /**
-        * Represents a write lock on the messages key
+        * Represents a write lock on the messages key.
+        *
+        * Will retry MessageCache::MSG_WAIT_TIMEOUT times, each operations having
+        * a timeout of MessageCache::MSG_LOCK_TIMEOUT.
         *
         * @param string $key
         * @return Boolean: success
index 65fe58f..da4621a 100644 (file)
  * @since 1.21
  */
 class RedisConnectionPool {
-       // Settings for all connections in this pool
-       protected $connectTimeout; // string; connection timeout
-       protected $persistent; // bool; whether connections persist
-       protected $password; // string; plaintext auth password
-       protected $serializer; // integer; the serializer to use (Redis::SERIALIZER_*)
+       /**
+        * @name Pool settings.
+        * Settings there are shared for any connection made in this pool.
+        * See the singleton() method documentation for more details.
+        * @{
+        */
+       /** @var string Connection timeout in seconds */
+       protected $connectTimeout;
+       /** @var string Plaintext auth password */
+       protected $password;
+       /** @var bool Whether connections persist */
+       protected $persistent;
+       /** @var integer Serializer to use (Redis::SERIALIZER_*) */
+       protected $serializer;
+       /** @} */
 
-       protected $idlePoolSize = 0; // integer; current idle pool size
+       /** @var integer Current idle pool size */
+       protected $idlePoolSize = 0;
 
        /** @var Array (server name => ((connection info array),...) */
        protected $connections = array();
        /** @var Array (server name => UNIX timestamp) */
        protected $downServers = array();
 
-       /** @var Array */
-       protected static $instances = array(); // (pool ID => RedisConnectionPool)
+       /** @var Array (pool ID => RedisConnectionPool) */
+       protected static $instances = array();
 
-       const SERVER_DOWN_TTL = 30; // integer; seconds to cache servers as "down"
+       /** integer; seconds to cache servers as "down". */
+       const SERVER_DOWN_TTL = 30;
 
        /**
         * @param array $options
index d75a126..ec9a62a 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Debug toolbar related code
+ * Debug toolbar related code.
  *
  * 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
@@ -21,7 +21,7 @@
  */
 
 /**
- * New debugger system that outputs a toolbar on page view
+ * New debugger system that outputs a toolbar on page view.
  *
  * By default, most methods do nothing ( self::$enabled = false ). You have
  * to explicitly call MWDebug::init() to enabled them.
@@ -35,28 +35,28 @@ class MWDebug {
        /**
         * Log lines
         *
-        * @var array
+        * @var array $log
         */
        protected static $log = array();
 
        /**
-        * Debug messages from wfDebug()
+        * Debug messages from wfDebug().
         *
-        * @var array
+        * @var array $debug
         */
        protected static $debug = array();
 
        /**
-        * Queries
+        * SQL statements of the databses queries.
         *
-        * @var array
+        * @var array $query
         */
        protected static $query = array();
 
        /**
         * Is the debugger enabled?
         *
-        * @var bool
+        * @var bool $enabled
         */
        protected static $enabled = false;
 
@@ -64,7 +64,7 @@ class MWDebug {
         * Array of functions that have already been warned, formatted
         * function-caller to prevent a buttload of warnings
         *
-        * @var array
+        * @var array $deprecationWarnings
         */
        protected static $deprecationWarnings = array();
 
index 911fffc..bb76d5a 100644 (file)
@@ -38,6 +38,13 @@ class PatrolLog {
         * @return bool
         */
        public static function record( $rc, $auto = false, User $user = null ) {
+               global $wgLogAutopatrol;
+
+               // do not log autopatrolled edits if setting disables it
+               if ( $auto && !$wgLogAutopatrol ) {
+                       return false;
+               }
+
                if ( !$rc instanceof RecentChange ) {
                        $rc = RecentChange::newFromId( $rc );
                        if ( !is_object( $rc ) ) {
index fd46c03..3b67554 100644 (file)
@@ -103,11 +103,8 @@ class SpecialPasswordReset extends FormSpecialPage {
        }
 
        public function alterForm( HTMLForm $form ) {
-               $form->setSubmitTextMsg( 'mailmypassword' );
-       }
-
-       protected function preText() {
                global $wgPasswordResetRoutes;
+
                $i = 0;
                if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) {
                        $i++;
@@ -119,7 +116,10 @@ class SpecialPasswordReset extends FormSpecialPage {
                        $i++;
                }
 
-               return $this->msg( 'passwordreset-pretext', $i )->parseAsBlock();
+               $message = ( $i > 1 ) ? 'passwordreset-text-many' : 'passwordreset-text-one';
+
+               $form->setHeaderText( $this->msg( $message, $i )->parseAsBlock() );
+               $form->setSubmitTextMsg( 'mailmypassword' );
        }
 
        /**
index 2e72f70..2f90d3c 100644 (file)
@@ -270,12 +270,9 @@ class UsercreateTemplateVForm extends BaseTemplate {
                // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
                $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
        ?>
-               <div>
-                       <div class="mw-benefits-icon <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>"></div>
-                       <div class="mw-number-text">
-                               <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
-                               <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
-                       </div>
+               <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
+                       <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
+                       <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
                </div>
        <?php
        }
index ea34363..92ea75c 100644 (file)
@@ -2953,12 +2953,16 @@ class Language {
        }
 
        /**
+        * Get all magic words from cache.
         * @return array
         */
        function getMagicWords() {
                return self::$dataCache->getItem( $this->mCode, 'magicWords' );
        }
 
+       /**
+        * Run the LanguageGetMagic hook once.
+        */
        protected function doMagicHook() {
                if ( $this->mMagicHookDone ) {
                        return;
@@ -2975,17 +2979,16 @@ class Language {
         * @param $mw
         */
        function getMagic( $mw ) {
-               $this->doMagicHook();
+               // Saves a function call
+               if ( ! $this->mMagicHookDone ) {
+                       $this->doMagicHook();
+               }
 
                if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
                        $rawEntry = $this->mMagicExtensions[$mw->mId];
                } else {
-                       $magicWords = $this->getMagicWords();
-                       if ( isset( $magicWords[$mw->mId] ) ) {
-                               $rawEntry = $magicWords[$mw->mId];
-                       } else {
-                               $rawEntry = false;
-                       }
+                       $rawEntry = self::$dataCache->getSubitem(
+                               $this->mCode, 'magicWords', $mw->mId );
                }
 
                if ( !is_array( $rawEntry ) ) {
index bc3a381..7d632ff 100644 (file)
@@ -1275,11 +1275,11 @@ You may have already successfully changed your password or requested a new tempo
 
 # Special:PasswordReset
 'passwordreset'                    => 'Reset password',
-'passwordreset-text'               => 'Complete this form to reset your password.',
+'passwordreset-text-one'           => 'Complete this form to reset your password.',
+'passwordreset-text-many'          => '{{PLURAL:$1|Enter one of the pieces of data to reset your password.}}',
 'passwordreset-legend'             => 'Reset password',
 'passwordreset-disabled'           => 'Password resets have been disabled on this wiki.',
 'passwordreset-emaildisabled'      => 'Email features have been disabled on this wiki.',
-'passwordreset-pretext'            => '{{PLURAL:$1||Enter one of the pieces of data below}}',
 'passwordreset-username'           => 'Username:',
 'passwordreset-domain'             => 'Domain:',
 'passwordreset-capture'            => 'View the resulting email?',
index daefa62..7dbfa33 100644 (file)
@@ -148,6 +148,8 @@ $digitTransformTable = array(
 
 $digitGroupingPattern = "##,##,###";
 
+$linkTrail = '/^((?:[a-z]|ક્|ખ્|ગ્|ઘ્|ચ્|છ્|જ્|ઝ્|ટ્|ઠ્|ડ્|ઢ્|ણ્|ત્|થ્|દ્|ધ્|ન્|પ્|ફ્|બ્|ભ્|મ્|ય્|ર્|લ્|વ્|સ્|શ્|ષ્|હ્|ળ્|ક્ષ્|જ્ઞ્|અ|આ|ઇ|ઈ|ઉ|ઊ|એ|ઐ|ઓ|ઔ|અં|અઃ|અઁ|ઍ|ઑ|ઋ|ઁ|઼|।|્|ા|િ|ી|ુ|ૂ|ે|ૈ|ો|ૌ|ં|ઃ|ઁ|ૅ|ૉ|ૃ)+)(.*)$/sDu';
+
 $messages = array(
 # User preference toggles
 'tog-underline' => 'કડીઓની નીચે લીટી (અંડરલાઇન) ઉમેરો:',
index ca59968..bfa8c18 100644 (file)
@@ -1348,7 +1348,10 @@ See also:
 # Special:PasswordReset
 'passwordreset' => 'Title of [[Special:PasswordReset]].
 {{Identical|Reset password}}',
-'passwordreset-text' => 'Text on [[Special:PasswordReset]]',
+'passwordreset-text-one' => 'Text on [[Special:PasswordReset]] that appears when there is only one way of resetting the password.',
+'passwordreset-text-many' => 'Text on [[Special:PasswordReset]] that appears when there are multiple ways of resetting the password.
+
+* $1 is the number of password reset routes.',
 'passwordreset-legend' => '{{Identical|Reset password}}',
 'passwordreset-disabled' => 'Used as error message in changing password.',
 'passwordreset-emaildisabled' => "Used as error message in changing password when site's email feature is disabled.",
@@ -8683,7 +8686,7 @@ See also:
 'specialpages-group-media' => '{{doc-special-group|like=[[Special:FilePath]], [[Special:MIMESearch]] and [[Special:Upload]]}}',
 'specialpages-group-users' => '{{doc-special-group|like=[[Special:ActiveUsers]], [[Special:Contributions]] and [[Special:ListGroupRights]]}}',
 'specialpages-group-highuse' => '{{doc-special-group|like=[[Special:MostCategories]], [[Special:MostLinked]] and [[Special:MostRevisions]]}}',
-'specialpages-group-pages' => '{{doc-special-group|like=[[Special:AllPages]], [[Special:PrefixIndex]], [[Special:Categories]], 
+'specialpages-group-pages' => '{{doc-special-group|like=[[Special:AllPages]], [[Special:PrefixIndex]], [[Special:Categories]],
 [[Special:Disambiguations]], etc}}',
 'specialpages-group-pagetools' => '{{doc-special-group|like=[[Special:MovePage]], [[Special:Undelete]], [[Special:WhatLinksHere]], [[Special:Export]] etc}}',
 'specialpages-group-wiki' => '{{doc-special-group|like=[[Special:Version]], [[Special:Statistics]], [[Special:LockDB]], etc}}',
index e6862ac..5d40c16 100644 (file)
@@ -176,6 +176,7 @@ FILE_PATTERNS          = *.c \
                          *.txt \
                          README
 RECURSIVE              = YES
+EXCLUDE                = {{EXCLUDE}}
 EXCLUDE_SYMLINKS       = YES
 EXCLUDE_PATTERNS       = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}}
 EXCLUDE_SYMBOLS        =
index fdd37e6..25d7bf4 100644 (file)
@@ -584,11 +584,11 @@ $wgMessageStructure = array(
        ),
        'passwordreset' => array(
                'passwordreset',
-               'passwordreset-text',
+               'passwordreset-text-one',
+               'passwordreset-text-many',
                'passwordreset-legend',
                'passwordreset-disabled',
                'passwordreset-emaildisabled',
-               'passwordreset-pretext',
                'passwordreset-username',
                'passwordreset-domain',
                'passwordreset-capture',
index 3439b74..9283264 100644 (file)
@@ -8,12 +8,6 @@
  * Usage:
  *   php mwdocgen.php
  *
- * KNOWN BUGS:
- *
- * - pass_thru seems to always use buffering (even with ob_implicit_flush()),
- * that make output slow when doxygen parses language files.
- * - the menu doesnt work, got disabled at revision 13740. Need to code it.
- *
  * 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
  * @version first release
  */
 
-#
-# Variables / Configuration
-#
-
-if ( PHP_SAPI != 'cli' ) {
-       echo 'Run "' . __FILE__ . '" from the command line.';
-       die( -1 );
-}
-
-/** Figure out the base directory for MediaWiki location */
-$mwPath = dirname( __DIR__ ) . DIRECTORY_SEPARATOR;
-
-/** doxygen binary script */
-$doxygenBin = 'doxygen';
-
-/** doxygen configuration template for mediawiki */
-$doxygenTemplate = $mwPath . 'maintenance/Doxyfile';
-
-/** doxygen input filter to tweak source file before they are parsed */
-$doxygenInputFilter = "php {$mwPath}maintenance/mwdoc-filter.php";
-
-/** where Phpdoc should output documentation */
-$doxyOutput = $mwPath . 'docs' . DIRECTORY_SEPARATOR;
-
-$doxyVersion = 'master';
-
-/** MediaWiki subpaths */
-$mwPathI = $mwPath . 'includes/';
-$mwPathL = $mwPath . 'languages/';
-$mwPathM = $mwPath . 'maintenance/';
-$mwPathS = $mwPath . 'skins/';
-
-/** Ignored paths relative to $mwPath */
-$mwExcludePaths = array(
-       'images',
-       'static',
-);
-
-/** Variable to get user input */
-$input = '';
-$excludePatterns = '';
-/** Whether to generates man pages: */
-$doxyGenerateMan = false;
-
-#
-# Functions
-#
-
-define( 'MEDIAWIKI', true );
-require_once "$mwPath/includes/GlobalFunctions.php";
-
-/**
- * Read a line from the shell
- * @param $prompt String
- * @return string
- */
-function readaline( $prompt = '' ) {
-       print $prompt;
-       $fp = fopen( "php://stdin", "r" );
-       $resp = trim( fgets( $fp, 1024 ) );
-       fclose( $fp );
-       return $resp;
-}
+require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Generate a configuration file given user parameters and return the temporary filename.
- * @param $doxygenTemplate String: full path for the template.
- * @param $outputDirectory String: directory where the stuff will be output.
- * @param $stripFromPath String: path that should be stripped out (usually mediawiki base path).
- * @param $currentVersion String: Version number of the software
- * @param $input String: Path to analyze.
- * @param $exclude String: Additionals path regex to exclude
- * @param $excludePatterns String: Additionals path regex to exclude
- *                 (LocalSettings.php, AdminSettings.php, .svn and .git directories are always excluded)
- * @param $doxyGenerateMan Boolean
- * @return string
+ * Maintenance script that builds doxygen documentation.
+ * @ingroup Maintenance
  */
-function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $input, $exclude, $excludePatterns, $doxyGenerateMan, $doxygenInputFilter ) {
-
-       $template = file_get_contents( $doxygenTemplate );
-       // Replace template placeholders by correct values.
-       $replacements = array(
-               '{{OUTPUT_DIRECTORY}}' => $outputDirectory,
-               '{{STRIP_FROM_PATH}}' => $stripFromPath,
-               '{{CURRENT_VERSION}}' => $currentVersion,
-               '{{INPUT}}' => $input,
-               '{{EXCLUDE}}' => $exclude,
-               '{{EXCLUDE_PATTERNS}}' => $excludePatterns,
-               '{{HAVE_DOT}}' => `which dot` ? 'YES' : 'NO',
-               '{{GENERATE_MAN}}' => $doxyGenerateMan ? 'YES' : 'NO',
-               '{{INPUT_FILTER}}' => $doxygenInputFilter,
-       );
-       $tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
-       $tmpFileName = tempnam( wfTempDir(), 'mwdocgen-' );
-       file_put_contents( $tmpFileName, $tmpCfg ) or die( "Could not write doxygen configuration to file $tmpFileName\n" );
-
-       return $tmpFileName;
-}
-
-#
-# Main !
-#
-
-unset( $file );
+class MWDocGen extends Maintenance {
+
+       /**
+        * Prepare Maintenance class
+        */
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = 'Build doxygen documentation';
+
+               $this->addOption( 'doxygen',
+                       'Path to doxygen',
+                       false, true );
+               $this->addOption( 'version',
+                       'Pass a MediaWiki version',
+                       false, true );
+               $this->addOption( 'generate-man',
+                       'Whether to generate man files' );
+               $this->addOption( 'file',
+                       'Only process given file (relative to $IP)',
+                       false, true );
+               $this->addOption( 'output',
+                       'Path to write doc to',
+                       false, true );
+               $this->addOption( 'no-extensions',
+                       'Ignore extensions' );
+       }
 
-if ( is_array( $argv ) ) {
-       for ( $i = 0; $i < count( $argv ); $i++ ) {
-               switch ( $argv[$i] ) {
-               case '--all':
-                       $input = 0;
-                       break;
-               case '--includes':
-                       $input = 1;
-                       break;
-               case '--languages':
-                       $input = 2;
-                       break;
-               case '--maintenance':
-                       $input = 3;
-                       break;
-               case '--skins':
-                       $input = 4;
-                       break;
-               case '--file':
-                       $input = 5;
-                       $i++;
-                       if ( isset( $argv[$i] ) ) {
-                               $file = $argv[$i];
-                       }
-                       break;
-               case '--no-extensions':
-                       $input = 6;
-                       break;
-               case '--output':
-                       $i++;
-                       if ( isset( $argv[$i] ) ) {
-                               $doxyOutput = realpath( $argv[$i] );
-                       }
-                       break;
-               case '--version':
-                       $i++;
-                       if ( isset( $argv[$i] ) ) {
-                               $doxyVersion = $argv[$i];
-                       }
-                       break;
-               case '--generate-man':
-                       $doxyGenerateMan = true;
-                       break;
-               case '--help':
-                       print <<<END
-Usage: php mwdocgen.php [<command>] [<options>]
+       public function getDbType() {
+               return Maintenance::DB_NONE;
+       }
 
-Commands:
-    --all           Process entire codebase
-    --includes      Process only files in includes/ dir
-    --languages     Process only files in languages/ dir
-    --maintenance   Process only files in maintenance/ dir
-    --skins         Process only files in skins/ dir
-    --file <file>   Process only the given file
-    --no-extensions Process everything but extensions directorys
+       protected function init() {
+               global $IP;
+
+               $this->doxygen = $this->getOption( 'doxygen', 'doxygen' );
+               $this->mwVersion = $this->getOption( 'version', 'master' );
+               $this->input = $IP . '/' . $this->getOption( 'file', '' );
+               $this->output = $this->getOption( 'output', "$IP/docs" );
+
+               $this->inputFilter = wfShellWikiCmd(
+                       $IP . '/maintenance/mwdoc-filter.php' );
+               $this->template = $IP . '/maintenance/Doxyfile';
+               $this->excludes = array(
+                       'images',
+                       'static',
+               );
+               $this->excludePatterns = array();
+               if ( $this->hasOption( 'no-extensions' ) ) {
+                       $this->excludePatterns[] = 'extensions';
+               }
 
-If no command is given, you will be prompted.
+               $this->doDot = `which dot`;
+               $this->doMan = $this->hasOption( 'generate-man' );
+       }
 
-Other options:
-    --output <dir>  Set output directory (default: $doxyOutput)
-    --generate-man  Generates man page documentation
-    --version       Project version to display in the outut (default: $doxyVersion)
-    --help          Show this help and exit.
+       public function execute() {
+               global $IP;
 
+               $this->init();
 
-END;
-                       exit( 0 );
-                       break;
+               # Build out directories we want to exclude
+               $exclude = '';
+               foreach ( $this->excludes as $item ) {
+                       $exclude .= " $IP/$item";
                }
-       }
-}
-
-// TODO : generate a list of paths ))
 
-if ( $input === '' ) {
-       echo <<<OPTIONS
-Several documentation possibilities:
- 0 : whole documentation (1 + 2 + 3 + 4)
- 1 : only includes
- 2 : only languages
- 3 : only maintenance
- 4 : only skins
- 5 : only a given file
- 6 : all but the extensions directory
-OPTIONS;
-       while ( !is_numeric( $input ) ) {
-               $input = readaline( "\nEnter your choice [0]:" );
-               if ( $input == '' ) {
-                       $input = 0;
+               $excludePatterns = implode( ' ', $this->excludePatterns );
+
+               $conf = strtr( file_get_contents( $this->template ),
+                       array(
+                               '{{OUTPUT_DIRECTORY}}' => $this->output,
+                               '{{STRIP_FROM_PATH}}' => $IP,
+                               '{{CURRENT_VERSION}}' => $this->mwVersion,
+                               '{{INPUT}}' => $this->input,
+                               '{{EXCLUDE}}' => $exclude,
+                               '{{EXCLUDE_PATTERNS}}' => $excludePatterns,
+                               '{{HAVE_DOT}}' => $this->doDot ? 'YES' : 'NO',
+                               '{{GENERATE_MAN}}' => $this->doMan ? 'YES' : 'NO',
+                               '{{INPUT_FILTER}}' => $this->inputFilter,
+                       )
+               );
+
+               $tmpFile = tempnam( wfTempDir(), 'MWDocGen-' );
+               if ( file_put_contents( $tmpFile, $conf ) === false ) {
+                       $this->error( "Could not write doxygen configuration to file $tmpFile\n",
+                               /** exit code: */ 1 );
                }
-       }
-}
-
-switch ( $input ) {
-case 0:
-       $input = $mwPath;
-       break;
-case 1:
-       $input = $mwPathI;
-       break;
-case 2:
-       $input = $mwPathL;
-       break;
-case 3:
-       $input = $mwPathM;
-       break;
-case 4:
-       $input = $mwPathS;
-       break;
-case 5:
-       if ( !isset( $file ) ) {
-               $file = readaline( "Enter file name $mwPath" );
-       }
-       $input = $mwPath . $file;
-       break;
-case 6:
-       $input = $mwPath;
-       $excludePatterns = 'extensions';
-}
 
-// Generate path exclusions
-$excludedPaths = $mwPath . join( " $mwPath", $mwExcludePaths );
-print "EXCLUDE: $excludedPaths\n\n";
+               $command = $this->doxygen . ' ' . $tmpFile;
+               $this->output( "Executing command:\n$command\n" );
 
-$generatedConf = generateConfigFile( $doxygenTemplate, $doxyOutput, $mwPath, $doxyVersion, $input, $excludedPaths, $excludePatterns, $doxyGenerateMan, $doxygenInputFilter );
-$command = $doxygenBin . ' ' . $generatedConf;
+               $exitcode = 1;
+               system( $command, $exitcode );
 
-echo <<<TEXT
+               $this->output( <<<TEXT
 ---------------------------------------------------
-Launching the command:
-
-$command
+Doxygen execution finished.
+Check above for possible errors.
 
+You might want to delete the temporary file:
+ $tmpFile
 ---------------------------------------------------
 
-TEXT;
-
-$exitcode = 1;
-passthru( $command, $exitcode );
+TEXT
+       );
 
-echo <<<TEXT
----------------------------------------------------
-Doxygen execution finished.
-Check above for possible errors.
+               if ( $exitcode !== 0 ) {
+                       $this->error( "Something went wrong (exit: $exitcode)\n",
+                               $exitcode );
+               }
 
-You might want to delete the temporary file $generatedConf
+       }
 
-TEXT;
+}
 
-exit( $exitcode );
+$maintClass = 'MWDocGen';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 2c251d7..f43505e 100644 (file)
Binary files a/resources/mediawiki.special/images/glyph-people-large.png and b/resources/mediawiki.special/images/glyph-people-large.png differ
index dd0648b..e9b8874 100644 (file)
Binary files a/resources/mediawiki.special/images/icon-contributors.png and b/resources/mediawiki.special/images/icon-contributors.png differ
index 263832b..f4ec247 100644 (file)
Binary files a/resources/mediawiki.special/images/icon-edits.png and b/resources/mediawiki.special/images/icon-edits.png differ
index 306f1f1..4b3d4ee 100644 (file)
Binary files a/resources/mediawiki.special/images/icon-lock.png and b/resources/mediawiki.special/images/icon-lock.png differ
index 0389c84..a406ce7 100644 (file)
Binary files a/resources/mediawiki.special/images/icon-pages.png and b/resources/mediawiki.special/images/icon-pages.png differ
index ef16a1d..11d00e7 100644 (file)
 }
 
 div.mw-createacct-benefits-container {
-       float: left
+       /* Keeps this column compact and close to the form, but tends to squish contents. */
+       float: left;
 }
 
 div.mw-createacct-benefits-container h2 {
        margin-bottom: 30px;
 }
 
-div.mw-benefits-icon {
-       display: inline-block;
-       padding: 0;
-       float: left;
-       width: 80px;
-       height: 75px;
-       margin-right: 15px;
-       border: 0;
-}
-
-.mw-benefits-icon.icon-edits {
+.mw-number-text.icon-edits {
        /* @embed */
-       background: url(images/icon-edits.png) no-repeat right;
+       background: url(images/icon-edits.png) no-repeat left center;
 }
 
-.mw-benefits-icon.icon-pages {
+.mw-number-text.icon-pages {
        /* @embed */
-       background: url(images/icon-pages.png) no-repeat right;
+       background: url(images/icon-pages.png) no-repeat left center;
 }
 
-.mw-benefits-icon.icon-contributors {
+.mw-number-text.icon-contributors {
        /* @embed */
-       background: url(images/icon-contributors.png) no-repeat right;
+       background: url(images/icon-contributors.png) no-repeat left center;
 }
 
 /* Special font for numbers in benefits*/
@@ -86,10 +77,13 @@ div.mw-number-text h3 {
        text-align: center;
 }
 
+/* Contains a number and explanatory text, with space for an icon */
 div.mw-number-text {
        display: block;
        font-size: 1.2em;
        color: #444;
        margin-top: 1em;
+       padding: 0 0 0 95px; /* 80px wide icon plus "margin" */
+       min-height: 75px; /* matches max icon height, ensures icon emblem is visible */
        text-align: center;
 }
index 87529b4..ac6f2a1 100644 (file)
                } );
        }
 
+       $( document ).ready( function() {
+               var $matrixTooltips = $( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
+               if ( $matrixTooltips.length ) {
+                       mw.loader.using( 'jquery.tipsy', function () {
+                               $matrixTooltips.tipsy();
+                       } );
+               }
+       } );
 }( mediaWiki, jQuery ) );
diff --git a/skins/common/images/question-small.png b/skins/common/images/question-small.png
new file mode 100644 (file)
index 0000000..590f0cb
Binary files /dev/null and b/skins/common/images/question-small.png differ
diff --git a/skins/common/images/question.svg b/skins/common/images/question.svg
new file mode 100644 (file)
index 0000000..2797305
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\r
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
+        width="21.059px" height="21.06px" viewBox="0 0 21.059 21.06" enable-background="new 0 0 21.059 21.06" xml:space="preserve">\r
+<path fill="#575757" d="M10.529,0C4.715,0,0,4.714,0,10.529s4.715,10.53,10.529,10.53c5.816,0,10.529-4.715,10.529-10.53\r
+       S16.346,0,10.529,0z M10.527,16.767c-0.861,0-1.498-0.688-1.498-1.516c0-0.862,0.637-1.534,1.498-1.534c0.828,0,1.5,0.672,1.5,1.534\r
+       C12.027,16.078,11.355,16.767,10.527,16.767z M12.664,10.255c-0.723,0.568-1,0.931-1,1.739v0.5H9.459v-0.603\r
+       c0-1.517,0.449-2.136,1.154-2.688c0.707-0.552,1.139-0.845,1.139-1.637c0-0.672-0.414-1.051-1.24-1.051\r
+       c-0.707,0-1.328,0.189-1.982,0.638L7.479,5.346c0.861-0.604,1.93-1.034,3.342-1.034c1.912,0,3.516,1.051,3.516,3.066\r
+       C14.336,8.808,13.543,9.566,12.664,10.255z"/>\r
+</svg>\r
index fb140b2..d946ce2 100644 (file)
@@ -235,6 +235,32 @@ td.mw-label {
 .prefsection table.mw-htmlform-matrix {
        width: auto;
 }
+
+.mw-icon-question {
+       /* @embed */
+       background: url('images/question-small.png') no-repeat;
+       /* SVG support using a transparent gradient to guarantee cross-browser
+        * compatibility (browsers able to understand gradient syntax support also SVG)
+        * lifted from #pt-login css rule in skins/vector/screen.css */
+       /* @embed */
+       background: -webkit-linear-gradient(transparent, transparent), url('images/question.svg') no-repeat;
+       /* @embed */
+       background: linear-gradient(transparent, transparent), url('images/question.svg') no-repeat;
+       background-size: 13px 13px;
+       display: inline-block;
+       height: 13px;
+       width: 13px;
+       margin-left: 4px;
+}
+
+.mw-icon-question:lang(ar),
+.mw-icon-question:lang(fa),
+.mw-icon-question:lang(ur) {
+       -webkit-transform: scaleX(-1);
+       -ms-transform: scaleX(-1);
+       transform: scaleX(-1);
+}
+
 td.mw-submit {
        white-space: nowrap;
 }
diff --git a/tests/phpunit/AutoLoaderTest.php b/tests/phpunit/AutoLoaderTest.php
deleted file mode 100644 (file)
index e49ea6d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-class AutoLoaderTest extends MediaWikiTestCase {
-
-       public function testAutoLoadConfig() {
-               $results = self::checkAutoLoadConf();
-
-               $this->assertEquals(
-                       $results['expected'],
-                       $results['actual']
-               );
-       }
-
-       protected static function checkAutoLoadConf() {
-               global $wgAutoloadLocalClasses, $wgAutoloadClasses, $IP;
-               $supportsParsekit = function_exists( 'parsekit_compile_file' );
-
-               // wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php
-               $expected = $wgAutoloadLocalClasses + $wgAutoloadClasses;
-               $actual = array();
-
-               $files = array_unique( $expected );
-
-               foreach ( $files as $file ) {
-                       // Only prefix $IP if it doesn't have it already.
-                       // Generally local classes don't have it, and those from extensions and test suites do.
-                       if ( substr( $file, 0, 1 ) != '/' && substr( $file, 1, 1 ) != ':' ) {
-                               $filePath = "$IP/$file";
-                       } else {
-                               $filePath = $file;
-                       }
-                       if ( $supportsParsekit ) {
-                               $parseInfo = parsekit_compile_file( "$filePath" );
-                               $classes = array_keys( $parseInfo['class_table'] );
-                       } else {
-                               $contents = file_get_contents( "$filePath" );
-                               $m = array();
-                               preg_match_all( '/\n\s*(?:final)?\s*(?:abstract)?\s*(?:class|interface)\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
-                               $classes = $m[1];
-                       }
-                       foreach ( $classes as $class ) {
-                               $actual[$class] = $file;
-                       }
-               }
-
-               return array(
-                       'expected' => $expected,
-                       'actual' => $actual,
-               );
-       }
-}
diff --git a/tests/phpunit/StructureTest.php b/tests/phpunit/StructureTest.php
deleted file mode 100644 (file)
index a942098..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
- * The tests here verify the structure of the code.  This is for outright bugs,
- * not just style issues.
- */
-
-class StructureTest extends MediaWikiTestCase {
-       /**
-        * Verify all files that appear to be tests have file names ending in
-        * Test.  If the file names do not end in Test, they will not be run.
-        * @group medium
-        */
-       public function testUnitTestFileNamesEndWithTest() {
-               if ( wfIsWindows() ) {
-                       $this->markTestSkipped( 'This test does not work on Windows' );
-               }
-               $rootPath = escapeshellarg( __DIR__ );
-               $testClassRegex = implode( '|', array(
-                       'ApiFormatTestBase',
-                       'ApiTestCase',
-                       'ApiQueryTestBase',
-                       'ApiQueryContinueTestBase',
-                       'MediaWikiLangTestCase',
-                       'MediaWikiTestCase',
-                       'PHPUnit_Framework_TestCase',
-                       'DumpTestCase',
-               ) );
-               $testClassRegex = "^class .* extends ($testClassRegex)";
-               $finder = "find $rootPath -name '*.php' '!' -name '*Test.php'" .
-                       " | xargs grep -El '$testClassRegex|function suite\('";
-
-               $results = null;
-               $exitCode = null;
-               exec( $finder, $results, $exitCode );
-
-               $this->assertEquals(
-                       0,
-                       $exitCode,
-                       'Verify find/grep command succeeds.'
-               );
-
-               $results = array_filter(
-                       $results,
-                       array( $this, 'filterSuites' )
-               );
-               $strip = strlen( $rootPath ) - 1;
-               foreach ( $results as $k => $v ) {
-                       $results[$k] = substr( $v, $strip );
-               }
-               $this->assertEquals(
-                       array(),
-                       $results,
-                       "Unit test file in $rootPath must end with Test."
-               );
-       }
-
-       /**
-        * Filter to remove testUnitTestFileNamesEndWithTest false positives.
-        */
-       public function filterSuites( $filename ) {
-               return strpos( $filename, __DIR__ . '/suites/' ) !== 0;
-       }
-}
diff --git a/tests/phpunit/includes/upload/UploadBaseTest.php b/tests/phpunit/includes/upload/UploadBaseTest.php
new file mode 100644 (file)
index 0000000..298420b
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+/**
+ * @group Upload
+ */
+class UploadBaseTest extends MediaWikiTestCase {
+       protected $upload;
+
+
+       protected function setUp() {
+               global $wgHooks;
+               parent::setUp();
+
+               $this->upload = new UploadTestHandler;
+               $this->hooks = $wgHooks;
+               $wgHooks['InterwikiLoadPrefix'][] = function ( $prefix, &$data ) {
+                       return false;
+               };
+       }
+
+       protected function tearDown() {
+               global $wgHooks;
+               $wgHooks = $this->hooks;
+
+               parent::tearDown();
+       }
+
+
+       /**
+        * First checks the return code
+        * of UploadBase::getTitle() and then the actual returned title
+        *
+        * @dataProvider provideTestTitleValidation
+        */
+       public function testTitleValidation( $srcFilename, $dstFilename, $code, $msg ) {
+               /* Check the result code */
+               $this->assertEquals( $code,
+                       $this->upload->testTitleValidation( $srcFilename ),
+                       "$msg code" );
+
+               /* If we expect a valid title, check the title itself. */
+               if ( $code == UploadBase::OK ) {
+                       $this->assertEquals( $dstFilename,
+                               $this->upload->getTitle()->getText(),
+                               "$msg text" );
+               }
+       }
+
+       /**
+        * Test various forms of valid and invalid titles that can be supplied.
+        */
+       public static function provideTestTitleValidation() {
+               return array(
+                       /* Test a valid title */
+                       array( 'ValidTitle.jpg', 'ValidTitle.jpg', UploadBase::OK,
+                               'upload valid title' ),
+                       /* A title with a slash */
+                       array( 'A/B.jpg', 'B.jpg', UploadBase::OK,
+                               'upload title with slash' ),
+                       /* A title with illegal char */
+                       array( 'A:B.jpg', 'A-B.jpg', UploadBase::OK,
+                               'upload title with colon' ),
+                       /* Stripping leading File: prefix */
+                       array( 'File:C.jpg', 'C.jpg', UploadBase::OK,
+                               'upload title with File prefix' ),
+                       /* Test illegal suggested title (r94601) */
+                       array( '%281%29.JPG', null, UploadBase::ILLEGAL_FILENAME,
+                               'illegal title for upload' ),
+                       /* A title without extension */
+                       array( 'A', null, UploadBase::FILETYPE_MISSING,
+                               'upload title without extension' ),
+                       /* A title with no basename */
+                       array( '.jpg', null, UploadBase::MIN_LENGTH_PARTNAME,
+                               'upload title without basename' ),
+                       /* A title that is longer than 255 bytes */
+                       array( str_repeat( 'a', 255 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
+                               'upload title longer than 255 bytes' ),
+                       /* A title that is longer than 240 bytes */
+                       array( str_repeat( 'a', 240 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
+                               'upload title longer than 240 bytes' ),
+               );
+       }
+
+       /**
+        * Test the upload verification functions
+        */
+       public function testVerifyUpload() {
+               /* Setup with zero file size */
+               $this->upload->initializePathInfo( '', '', 0 );
+               $result = $this->upload->verifyUpload();
+               $this->assertEquals( UploadBase::EMPTY_FILE,
+                       $result['status'],
+                       'upload empty file' );
+       }
+
+       // Helper used to create an empty file of size $size.
+       private function createFileOfSize( $size ) {
+               $filename = tempnam( wfTempDir(), "mwuploadtest" );
+
+               $fh = fopen( $filename, 'w' );
+               ftruncate( $fh, $size );
+               fclose( $fh );
+
+               return $filename;
+       }
+
+       /**
+        * test uploading a 100 bytes file with $wgMaxUploadSize = 100
+        *
+        * This method should be abstracted so we can test different settings.
+        */
+
+       public function testMaxUploadSize() {
+               global $wgMaxUploadSize;
+               $savedGlobal = $wgMaxUploadSize; // save global
+               global $wgFileExtensions;
+               $wgFileExtensions[] = 'txt';
+
+               $wgMaxUploadSize = 100;
+
+               $filename = $this->createFileOfSize( $wgMaxUploadSize );
+               $this->upload->initializePathInfo( basename( $filename ) . '.txt', $filename, 100 );
+               $result = $this->upload->verifyUpload();
+               unlink( $filename );
+
+               $this->assertEquals(
+                       array( 'status' => UploadBase::OK ), $result );
+
+               $wgMaxUploadSize = $savedGlobal; // restore global
+       }
+}
+
+class UploadTestHandler extends UploadBase {
+       public function initializeFromRequest( &$request ) {
+       }
+
+       public function testTitleValidation( $name ) {
+               $this->mTitle = false;
+               $this->mDesiredDestName = $name;
+               $this->mTitleError = UploadBase::OK;
+               $this->getTitle();
+
+               return $this->mTitleError;
+       }
+}
diff --git a/tests/phpunit/includes/upload/UploadTest.php b/tests/phpunit/includes/upload/UploadTest.php
deleted file mode 100644 (file)
index 94c78dc..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-/**
- * @group Upload
- */
-class UploadTest extends MediaWikiTestCase {
-       protected $upload;
-
-
-       protected function setUp() {
-               global $wgHooks;
-               parent::setUp();
-
-               $this->upload = new UploadTestHandler;
-               $this->hooks = $wgHooks;
-               $wgHooks['InterwikiLoadPrefix'][] = function ( $prefix, &$data ) {
-                       return false;
-               };
-       }
-
-       protected function tearDown() {
-               global $wgHooks;
-               $wgHooks = $this->hooks;
-
-               parent::tearDown();
-       }
-
-
-       /**
-        * First checks the return code
-        * of UploadBase::getTitle() and then the actual returned title
-        *
-        * @dataProvider provideTestTitleValidation
-        */
-       public function testTitleValidation( $srcFilename, $dstFilename, $code, $msg ) {
-               /* Check the result code */
-               $this->assertEquals( $code,
-                       $this->upload->testTitleValidation( $srcFilename ),
-                       "$msg code" );
-
-               /* If we expect a valid title, check the title itself. */
-               if ( $code == UploadBase::OK ) {
-                       $this->assertEquals( $dstFilename,
-                               $this->upload->getTitle()->getText(),
-                               "$msg text" );
-               }
-       }
-
-       /**
-        * Test various forms of valid and invalid titles that can be supplied.
-        */
-       public static function provideTestTitleValidation() {
-               return array(
-                       /* Test a valid title */
-                       array( 'ValidTitle.jpg', 'ValidTitle.jpg', UploadBase::OK,
-                               'upload valid title' ),
-                       /* A title with a slash */
-                       array( 'A/B.jpg', 'B.jpg', UploadBase::OK,
-                               'upload title with slash' ),
-                       /* A title with illegal char */
-                       array( 'A:B.jpg', 'A-B.jpg', UploadBase::OK,
-                               'upload title with colon' ),
-                       /* Stripping leading File: prefix */
-                       array( 'File:C.jpg', 'C.jpg', UploadBase::OK,
-                               'upload title with File prefix' ),
-                       /* Test illegal suggested title (r94601) */
-                       array( '%281%29.JPG', null, UploadBase::ILLEGAL_FILENAME,
-                               'illegal title for upload' ),
-                       /* A title without extension */
-                       array( 'A', null, UploadBase::FILETYPE_MISSING,
-                               'upload title without extension' ),
-                       /* A title with no basename */
-                       array( '.jpg', null, UploadBase::MIN_LENGTH_PARTNAME,
-                               'upload title without basename' ),
-                       /* A title that is longer than 255 bytes */
-                       array( str_repeat( 'a', 255 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
-                               'upload title longer than 255 bytes' ),
-                       /* A title that is longer than 240 bytes */
-                       array( str_repeat( 'a', 240 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
-                               'upload title longer than 240 bytes' ),
-               );
-       }
-
-       /**
-        * Test the upload verification functions
-        */
-       public function testVerifyUpload() {
-               /* Setup with zero file size */
-               $this->upload->initializePathInfo( '', '', 0 );
-               $result = $this->upload->verifyUpload();
-               $this->assertEquals( UploadBase::EMPTY_FILE,
-                       $result['status'],
-                       'upload empty file' );
-       }
-
-       // Helper used to create an empty file of size $size.
-       private function createFileOfSize( $size ) {
-               $filename = tempnam( wfTempDir(), "mwuploadtest" );
-
-               $fh = fopen( $filename, 'w' );
-               ftruncate( $fh, $size );
-               fclose( $fh );
-
-               return $filename;
-       }
-
-       /**
-        * test uploading a 100 bytes file with $wgMaxUploadSize = 100
-        *
-        * This method should be abstracted so we can test different settings.
-        */
-
-       public function testMaxUploadSize() {
-               global $wgMaxUploadSize;
-               $savedGlobal = $wgMaxUploadSize; // save global
-               global $wgFileExtensions;
-               $wgFileExtensions[] = 'txt';
-
-               $wgMaxUploadSize = 100;
-
-               $filename = $this->createFileOfSize( $wgMaxUploadSize );
-               $this->upload->initializePathInfo( basename( $filename ) . '.txt', $filename, 100 );
-               $result = $this->upload->verifyUpload();
-               unlink( $filename );
-
-               $this->assertEquals(
-                       array( 'status' => UploadBase::OK ), $result );
-
-               $wgMaxUploadSize = $savedGlobal; // restore global
-       }
-}
-
-class UploadTestHandler extends UploadBase {
-       public function initializeFromRequest( &$request ) {
-       }
-
-       public function testTitleValidation( $name ) {
-               $this->mTitle = false;
-               $this->mDesiredDestName = $name;
-               $this->mTitleError = UploadBase::OK;
-               $this->getTitle();
-
-               return $this->mTitleError;
-       }
-}
diff --git a/tests/phpunit/resources/ResourcesTest.php b/tests/phpunit/resources/ResourcesTest.php
deleted file mode 100644 (file)
index 3af805a..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * Sanity checks for making sure registered resources are sane.
- *
- * @file
- * @author Niklas Laxström, 2012
- * @author Antoine Musso, 2012
- * @author Santhosh Thottingal, 2012
- * @author Timo Tijhof, 2012
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-class ResourcesTest extends MediaWikiTestCase {
-
-       /**
-        * @dataProvider provideResourceFiles
-        */
-       public function testFileExistence( $filename, $module, $resource ) {
-               $this->assertFileExists( $filename,
-                       "File '$resource' referenced by '$module' must exist."
-               );
-       }
-
-       /**
-        * This ask the ResouceLoader for all registered files from modules
-        * created by ResourceLoaderFileModule (or one of its descendants).
-        *
-        *
-        * Since the raw data is stored in protected properties, we have to
-        * overrride this through ReflectionObject methods.
-        */
-       public static function provideResourceFiles() {
-               global $wgEnableJavaScriptTest;
-
-               // Test existance of test suite files as well
-               // (can't use setUp or setMwGlobals because providers are static)
-               $live_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
-               $wgEnableJavaScriptTest = true;
-
-               // Array with arguments for the test function
-               $cases = array();
-
-               // Initialize ResourceLoader
-               $rl = new ResourceLoader();
-
-               // See also ResourceLoaderFileModule::__construct
-               $filePathProps = array(
-                       // Lists of file paths
-                       'lists' => array(
-                               'scripts',
-                               'debugScripts',
-                               'loaderScripts',
-                               'styles',
-                       ),
-
-                       // Collated lists of file paths
-                       'nested-lists' => array(
-                               'languageScripts',
-                               'skinScripts',
-                               'skinStyles',
-                       ),
-               );
-
-               foreach ( $rl->getModuleNames() as $moduleName ) {
-                       $module = $rl->getModule( $moduleName );
-                       if ( !$module instanceof ResourceLoaderFileModule ) {
-                               continue;
-                       }
-
-                       $reflectedModule = new ReflectionObject( $module );
-
-                       $files = array();
-
-                       foreach ( $filePathProps['lists'] as $propName ) {
-                               $property = $reflectedModule->getProperty( $propName );
-                               $property->setAccessible( true );
-                               $list = $property->getValue( $module );
-                               foreach ( $list as $key => $value ) {
-                                       // 'scripts' are numeral arrays.
-                                       // 'styles' can be numeral or associative.
-                                       // In case of associative the key is the file path
-                                       // and the value is the 'media' attribute.
-                                       if ( is_int( $key ) ) {
-                                               $files[] = $value;
-                                       } else {
-                                               $files[] = $key;
-                                       }
-                               }
-                       }
-
-                       foreach ( $filePathProps['nested-lists'] as $propName ) {
-                               $property = $reflectedModule->getProperty( $propName );
-                               $property->setAccessible( true );
-                               $lists = $property->getValue( $module );
-                               foreach ( $lists as $list ) {
-                                       foreach ( $list as $key => $value ) {
-                                               // We need the same filter as for 'lists',
-                                               // due to 'skinStyles'.
-                                               if ( is_int( $key ) ) {
-                                                       $files[] = $value;
-                                               } else {
-                                                       $files[] = $key;
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Get method for resolving the paths to full paths
-                       $method = $reflectedModule->getMethod( 'getLocalPath' );
-                       $method->setAccessible( true );
-
-                       // Populate cases
-                       foreach ( $files as $file ) {
-                               $cases[] = array(
-                                       $method->invoke( $module, $file ),
-                                       $module->getName(),
-                                       $file,
-                               );
-                       }
-               }
-
-               // Restore settings
-               $wgEnableJavaScriptTest = $live_wgEnableJavaScriptTest;
-
-               return $cases;
-       }
-}
diff --git a/tests/phpunit/structure/AutoLoaderTest.php b/tests/phpunit/structure/AutoLoaderTest.php
new file mode 100644 (file)
index 0000000..e49ea6d
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+class AutoLoaderTest extends MediaWikiTestCase {
+
+       public function testAutoLoadConfig() {
+               $results = self::checkAutoLoadConf();
+
+               $this->assertEquals(
+                       $results['expected'],
+                       $results['actual']
+               );
+       }
+
+       protected static function checkAutoLoadConf() {
+               global $wgAutoloadLocalClasses, $wgAutoloadClasses, $IP;
+               $supportsParsekit = function_exists( 'parsekit_compile_file' );
+
+               // wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php
+               $expected = $wgAutoloadLocalClasses + $wgAutoloadClasses;
+               $actual = array();
+
+               $files = array_unique( $expected );
+
+               foreach ( $files as $file ) {
+                       // Only prefix $IP if it doesn't have it already.
+                       // Generally local classes don't have it, and those from extensions and test suites do.
+                       if ( substr( $file, 0, 1 ) != '/' && substr( $file, 1, 1 ) != ':' ) {
+                               $filePath = "$IP/$file";
+                       } else {
+                               $filePath = $file;
+                       }
+                       if ( $supportsParsekit ) {
+                               $parseInfo = parsekit_compile_file( "$filePath" );
+                               $classes = array_keys( $parseInfo['class_table'] );
+                       } else {
+                               $contents = file_get_contents( "$filePath" );
+                               $m = array();
+                               preg_match_all( '/\n\s*(?:final)?\s*(?:abstract)?\s*(?:class|interface)\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
+                               $classes = $m[1];
+                       }
+                       foreach ( $classes as $class ) {
+                               $actual[$class] = $file;
+                       }
+               }
+
+               return array(
+                       'expected' => $expected,
+                       'actual' => $actual,
+               );
+       }
+}
diff --git a/tests/phpunit/structure/ResourcesTest.php b/tests/phpunit/structure/ResourcesTest.php
new file mode 100644 (file)
index 0000000..3af805a
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Sanity checks for making sure registered resources are sane.
+ *
+ * @file
+ * @author Niklas Laxström, 2012
+ * @author Antoine Musso, 2012
+ * @author Santhosh Thottingal, 2012
+ * @author Timo Tijhof, 2012
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+class ResourcesTest extends MediaWikiTestCase {
+
+       /**
+        * @dataProvider provideResourceFiles
+        */
+       public function testFileExistence( $filename, $module, $resource ) {
+               $this->assertFileExists( $filename,
+                       "File '$resource' referenced by '$module' must exist."
+               );
+       }
+
+       /**
+        * This ask the ResouceLoader for all registered files from modules
+        * created by ResourceLoaderFileModule (or one of its descendants).
+        *
+        *
+        * Since the raw data is stored in protected properties, we have to
+        * overrride this through ReflectionObject methods.
+        */
+       public static function provideResourceFiles() {
+               global $wgEnableJavaScriptTest;
+
+               // Test existance of test suite files as well
+               // (can't use setUp or setMwGlobals because providers are static)
+               $live_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
+               $wgEnableJavaScriptTest = true;
+
+               // Array with arguments for the test function
+               $cases = array();
+
+               // Initialize ResourceLoader
+               $rl = new ResourceLoader();
+
+               // See also ResourceLoaderFileModule::__construct
+               $filePathProps = array(
+                       // Lists of file paths
+                       'lists' => array(
+                               'scripts',
+                               'debugScripts',
+                               'loaderScripts',
+                               'styles',
+                       ),
+
+                       // Collated lists of file paths
+                       'nested-lists' => array(
+                               'languageScripts',
+                               'skinScripts',
+                               'skinStyles',
+                       ),
+               );
+
+               foreach ( $rl->getModuleNames() as $moduleName ) {
+                       $module = $rl->getModule( $moduleName );
+                       if ( !$module instanceof ResourceLoaderFileModule ) {
+                               continue;
+                       }
+
+                       $reflectedModule = new ReflectionObject( $module );
+
+                       $files = array();
+
+                       foreach ( $filePathProps['lists'] as $propName ) {
+                               $property = $reflectedModule->getProperty( $propName );
+                               $property->setAccessible( true );
+                               $list = $property->getValue( $module );
+                               foreach ( $list as $key => $value ) {
+                                       // 'scripts' are numeral arrays.
+                                       // 'styles' can be numeral or associative.
+                                       // In case of associative the key is the file path
+                                       // and the value is the 'media' attribute.
+                                       if ( is_int( $key ) ) {
+                                               $files[] = $value;
+                                       } else {
+                                               $files[] = $key;
+                                       }
+                               }
+                       }
+
+                       foreach ( $filePathProps['nested-lists'] as $propName ) {
+                               $property = $reflectedModule->getProperty( $propName );
+                               $property->setAccessible( true );
+                               $lists = $property->getValue( $module );
+                               foreach ( $lists as $list ) {
+                                       foreach ( $list as $key => $value ) {
+                                               // We need the same filter as for 'lists',
+                                               // due to 'skinStyles'.
+                                               if ( is_int( $key ) ) {
+                                                       $files[] = $value;
+                                               } else {
+                                                       $files[] = $key;
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Get method for resolving the paths to full paths
+                       $method = $reflectedModule->getMethod( 'getLocalPath' );
+                       $method->setAccessible( true );
+
+                       // Populate cases
+                       foreach ( $files as $file ) {
+                               $cases[] = array(
+                                       $method->invoke( $module, $file ),
+                                       $module->getName(),
+                                       $file,
+                               );
+                       }
+               }
+
+               // Restore settings
+               $wgEnableJavaScriptTest = $live_wgEnableJavaScriptTest;
+
+               return $cases;
+       }
+}
diff --git a/tests/phpunit/structure/StructureTest.php b/tests/phpunit/structure/StructureTest.php
new file mode 100644 (file)
index 0000000..df00d4d
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * The tests here verify the structure of the code.  This is for outright bugs,
+ * not just style issues.
+ */
+
+class StructureTest extends MediaWikiTestCase {
+       /**
+        * Verify all files that appear to be tests have file names ending in
+        * Test.  If the file names do not end in Test, they will not be run.
+        * @group medium
+        */
+       public function testUnitTestFileNamesEndWithTest() {
+               if ( wfIsWindows() ) {
+                       $this->markTestSkipped( 'This test does not work on Windows' );
+               }
+               $rootPath = escapeshellarg( __DIR__ . '/..' );
+               $testClassRegex = implode( '|', array(
+                       'ApiFormatTestBase',
+                       'ApiTestCase',
+                       'ApiQueryTestBase',
+                       'ApiQueryContinueTestBase',
+                       'MediaWikiLangTestCase',
+                       'MediaWikiTestCase',
+                       'PHPUnit_Framework_TestCase',
+                       'DumpTestCase',
+               ) );
+               $testClassRegex = "^class .* extends ($testClassRegex)";
+               $finder = "find $rootPath -name '*.php' '!' -name '*Test.php'" .
+                       " | xargs grep -El '$testClassRegex|function suite\('";
+
+               $results = null;
+               $exitCode = null;
+               exec( $finder, $results, $exitCode );
+
+               $this->assertEquals(
+                       0,
+                       $exitCode,
+                       'Verify find/grep command succeeds.'
+               );
+
+               $results = array_filter(
+                       $results,
+                       array( $this, 'filterSuites' )
+               );
+               $strip = strlen( $rootPath ) - 1;
+               foreach ( $results as $k => $v ) {
+                       $results[$k] = substr( $v, $strip );
+               }
+               $this->assertEquals(
+                       array(),
+                       $results,
+                       "Unit test file in $rootPath must end with Test."
+               );
+       }
+
+       /**
+        * Filter to remove testUnitTestFileNamesEndWithTest false positives.
+        */
+       public function filterSuites( $filename ) {
+               return strpos( $filename, __DIR__ . '/../suites/' ) !== 0;
+       }
+}
index 56f6447..844c853 100644 (file)
@@ -29,7 +29,7 @@
                        <directory>maintenance</directory>
                </testsuite>
                <testsuite name="structure">
-                       <file>StructureTest.php</file>
+                       <directory>structure</directory>
                </testsuite>
                <testsuite name="uploadfromurl">
                        <file>suites/UploadFromUrlTestSuite.php</file>