* $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.
/** 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;
* 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 {
}
$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";
*/
/**
- *
+ * 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
*/
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;
/**
*/
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;
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 );
}
/**
- * 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
* @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
<?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
*/
/**
- * 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.
/**
* 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;
* 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();
* @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 ) ) {
}
public function alterForm( HTMLForm $form ) {
- $form->setSubmitTextMsg( 'mailmypassword' );
- }
-
- protected function preText() {
global $wgPasswordResetRoutes;
+
$i = 0;
if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) {
$i++;
$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' );
}
/**
// 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
}
}
/**
+ * 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;
* @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 ) ) {
# 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?',
$digitGroupingPattern = "##,##,###";
+$linkTrail = '/^((?:[a-z]|ક્|ખ્|ગ્|ઘ્|ચ્|છ્|જ્|ઝ્|ટ્|ઠ્|ડ્|ઢ્|ણ્|ત્|થ્|દ્|ધ્|ન્|પ્|ફ્|બ્|ભ્|મ્|ય્|ર્|લ્|વ્|સ્|શ્|ષ્|હ્|ળ્|ક્ષ્|જ્ઞ્|અ|આ|ઇ|ઈ|ઉ|ઊ|એ|ઐ|ઓ|ઔ|અં|અઃ|અઁ|ઍ|ઑ|ઋ|ઁ|઼|।|્|ા|િ|ી|ુ|ૂ|ે|ૈ|ો|ૌ|ં|ઃ|ઁ|ૅ|ૉ|ૃ)+)(.*)$/sDu';
+
$messages = array(
# User preference toggles
'tog-underline' => 'કડીઓની નીચે લીટી (અંડરલાઇન) ઉમેરો:',
# 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.",
'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}}',
*.txt \
README
RECURSIVE = YES
+EXCLUDE = {{EXCLUDE}}
EXCLUDE_SYMLINKS = YES
EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}}
EXCLUDE_SYMBOLS =
),
'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',
* 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;
}
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*/
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;
}
} );
}
+ $( document ).ready( function() {
+ var $matrixTooltips = $( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
+ if ( $matrixTooltips.length ) {
+ mw.loader.using( 'jquery.tipsy', function () {
+ $matrixTooltips.tipsy();
+ } );
+ }
+ } );
}( mediaWiki, jQuery ) );
--- /dev/null
+<?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
.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;
}
+++ /dev/null
-<?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,
- );
- }
-}
+++ /dev/null
-<?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;
- }
-}
--- /dev/null
+<?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;
+ }
+}
+++ /dev/null
-<?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;
- }
-}
+++ /dev/null
-<?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;
- }
-}
--- /dev/null
+<?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,
+ );
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
<directory>maintenance</directory>
</testsuite>
<testsuite name="structure">
- <file>StructureTest.php</file>
+ <directory>structure</directory>
</testsuite>
<testsuite name="uploadfromurl">
<file>suites/UploadFromUrlTestSuite.php</file>