* HTMLForm will turn multiselect checkboxes into a Chosen interface when setting cssclass 'mw-chosen'
* rebuildLocalisationCache learned --lang option. Let you rebuild l10n caches
of the specified languages instead of all of them.
+* mediawiki.log: Added log.warn wrapper (uses console.warn and console.trace).
+* mediawiki.log: Implemented log.deprecate. This method defines a property and
+ uses ES5 getter/setter to emit a warning when they are used.
=== Bug fixes in 1.22 ===
* Disable Special:PasswordReset when $wgEnableEmail. Previously one could still
apply the new LanguageLinks hook, and thus only consider language links
stored in the database.
* (bug 47219) Allow specifying change type of Wikipedia feed items
+* prop=imageinfo now allows setting iiurlheight without setting iiurlwidth
=== Languages updated in 1.22===
* A new Special:Redirect page was added, providing lookup by revision ID,
user ID, or file name. The old Special:Filepath page was reimplemented
to redirect through Special:Redirect.
+* Monobook: Removed the old conditional stylesheets for Opera 6, 7 and 9.
== Compatibility ==
}
if ( !$loaded ) {
throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
- "to insert a user row, but then it doesn't exist when we select it!" );
+ "to insert user '{$this->mName}' row, but it was not present in select!" );
}
return Status::newFatal( 'userexists' );
}
public function getScale( $params ) {
$p = $this->getModulePrefix();
- // Height and width.
- if ( $params['urlheight'] != -1 && $params['urlwidth'] == -1 ) {
- $this->dieUsage( "{$p}urlheight cannot be used without {$p}urlwidth", "{$p}urlwidth" );
- }
-
if ( $params['urlwidth'] != -1 ) {
$scale = array();
$scale['width'] = $params['urlwidth'];
$scale['height'] = $params['urlheight'];
+ } elseif ( $params['urlheight'] != -1 ) {
+ // Height is specified but width isn't
+ // Don't set $scale['width']; this signals mergeThumbParams() to fill it with the image's width
+ $scale = array();
+ $scale['height'] = $params['urlheight'];
} else {
$scale = null;
if ( $params['urlparam'] ) {
$this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" );
}
- return $scale;
}
return $scale;
* @return Array of parameters for transform.
*/
protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
+
+ if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
+ // Populate the width with the image's width, so only the height restriction applies
+ $thumbParams['width'] = $image->getWidth();
+ }
+
if ( !$otherParams ) {
return $thumbParams;
}
if ( isset( $paramList['width'] ) ) {
if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
- $this->dieUsage( "{$p}urlparam had width of {$paramList['width']} but "
- . "{$p}urlwidth was {$thumbParams['width']}", "urlparam_urlwidth_mismatch" );
+ $this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
+ . "in favor of width value derived from {$p}urlwidth/{$p}urlheight ({$thumbParams['width']})" );
}
}
'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
'For performance reasons if this option is used, ' .
'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.' ),
- 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth",
+ 'urlheight' => "Similar to {$p}urlwidth.",
'urlparam' => array( "A handler specific parameter string. For example, pdf's ",
"might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ),
'limit' => 'How many image revisions to return per image',
array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ),
array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ),
array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ),
- array( 'code' => 'urlparam_urlwidth_mismatch', 'info' => "The width set in {$p}urlparm doesn't " .
- "match the one in {$p}urlwidth" ),
) );
}
$this->getVar( '_InstallUser' ),
$this->getVar( '_InstallPassword' ),
false,
- false,
0,
$this->getVar( 'wgDBprefix' )
);
$this->getVar( 'wgDBuser' ),
$this->getVar( 'wgDBpassword' ),
false,
- false,
0,
$this->getVar( 'wgDBprefix' )
);
$dbUser,
$password,
false,
- false,
0,
$this->getVar( 'wgDBprefix' )
);
$user = $this->getUser();
+ $suggestedDurations = self::getSuggestedDurations();
+
$a = array(
'Target' => array(
'type' => 'text',
'validation-callback' => array( __CLASS__, 'validateTargetField' ),
),
'Expiry' => array(
- 'type' => !count( self::getSuggestedDurations() ) ? 'text' : 'selectorother',
+ 'type' => !count( $suggestedDurations ) ? 'text' : 'selectorother',
'label-message' => 'ipbexpiry',
'required' => true,
'tabindex' => '2',
- 'options' => self::getSuggestedDurations(),
+ 'options' => $suggestedDurations,
'other' => $this->msg( 'ipbother' )->text(),
'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
),
if ( $this->getRequest()->getCheck( 'success' ) ) {
$out->wrapWikiMsg(
- "<div class=\"successbox\"><strong>\n$1\n</strong></div><div id=\"mw-pref-clear\"></div>",
+ "<div class=\"successbox\">\n$1\n</div>",
'savedprefs'
);
}
*/
public static function isValidCode( $code ) {
static $cache = array();
- if( isset( $cache[$code] ) ) {
+ if ( isset( $cache[$code] ) ) {
return $cache[$code];
}
// People think language codes are html safe, so enforce it.
// Ideally we should only allow a-zA-Z0-9-
// but, .+ and other chars are often used for {{int:}} hacks
// see bugs 37564, 37587, 36938
- $return =
+ $cache[$code] =
strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
&& !preg_match( Title::getTitleInvalidRegex(), $code );
- $cache[ $code ] = $return;
- return $return;
+
+ return $cache[$code];
}
/**
public function __construct() {
parent::__construct();
$this->mDescription = "Deletes all pages in the MediaWiki namespace that are equal to the default message";
- $this->addOption( 'delete', 'Actually delete the pages' );
- $this->addOption( 'delete-talk', 'Don\'t leave orphaned talk pages behind' );
+ $this->addOption( 'delete', 'Actually delete the pages (default: dry run)' );
+ $this->addOption( 'delete-talk', 'Don\'t leave orphaned talk pages behind during deletion' );
$this->addOption( 'lang-code', 'Check for subpages of this lang-code (default: root page against content language)', false, true );
}
}
$this->output( "\n{$relevantPages} pages in the MediaWiki namespace override messages." );
- $this->output( "\n{$equalPages} pages are equal to the default message ({$equalPagesTalks} talk pages).\n" );
+ $this->output( "\n{$equalPages} pages are equal to the default message (+ {$equalPagesTalks} talk pages).\n" );
if ( !$doDelete ) {
- $this->output( "\nRun the script again with --delete to delete these pages" );
+ $list = '';
+ foreach ( $results as $result ) {
+ $title = Title::makeTitle( NS_MEDIAWIKI, $result['title'] );
+ $list .= "* [[$title]]\n";
+ if ( $result['hasTalk'] ) {
+ $title = Title::makeTitle( NS_MEDIAWIKI_TALK, $result['title'] );
+ $list .= "* [[$title]]\n";
+ }
+ }
+ $this->output( "\nList:\n$list\nRun the script again with --delete to delete these pages" );
if ( $equalPagesTalks !== 0 ) {
$this->output( " (include --delete-talk to also delete the talk pages)" );
}
$user->addGroup( 'bot' );
// Handle deletion
- $this->output( "\n...deleting equal messages (this may take a long time!)...", 'msg' );
+ $this->output( "\n...deleting equal messages (this may take a long time!)..." );
$dbw = wfGetDB( DB_MASTER );
foreach ( $results as $result ) {
wfWaitForSlaves();
$dbw->ping();
$dbw->begin( __METHOD__ );
$title = Title::makeTitle( NS_MEDIAWIKI, $result['title'] );
+ $this->output( "\n* [[$title]]" );
$page = WikiPage::factory( $title );
$error = ''; // Passed by ref
$page->doDeleteArticle( 'No longer required', false, 0, false, $error, $user );
if ( $result['hasTalk'] && $doDeleteTalk ) {
$title = Title::makeTitle( NS_MEDIAWIKI_TALK, $result['title'] );
+ $this->output( "\n* [[$title]]" );
$page = WikiPage::factory( $title );
$error = ''; // Passed by ref
$page->doDeleteArticle( 'Orphaned talk page of no longer required message', false, 0, false, $error, $user );
}
$dbw->commit( __METHOD__ );
}
- $this->output( "done!\n", 'msg' );
+ $this->output( "\n\ndone!\n" );
}
}
"mw.Map",
"mw.Message",
"mw.loader",
+ "mw.log",
"mw.html",
"mw.html.Cdata",
"mw.html.Raw"
"mw.Title",
"mw.notification",
"mw.util",
- "mw.plugin.notify"
+ "mw.plugin.*"
]
},
{
"--": [
"./external.js",
"../../resources/mediawiki/mediawiki.js",
+ "../../resources/mediawiki/mediawiki.log.js",
"../../resources/mediawiki/mediawiki.util.js",
"../../resources/mediawiki/mediawiki.Title.js",
"../../resources/mediawiki/mediawiki.notify.js",
*/
function toggleLinkPremade( $that, e, options ) {
var $collapsible = $that.eq( 0 ).closest( '.mw-collapsible' );
- options = $.extend( { toggleClasses: true }, options );
+ options = $.extend( { toggleClasses: true, linksPassthru: true }, options );
togglingHandler( $that, $collapsible, e, options );
}
* @param {jQuery} $collapsible
*/
function toggleLinkCustom( $that, e, options, $collapsible ) {
- options = $.extend( { linksPassthru: true }, options );
togglingHandler( $that, $collapsible, e, options );
}
/* Public Members */
/**
- * Dummy function which in debug mode can be replaced with a function that
- * emulates console.log in console-less environments.
+ * Dummy placeholder for {@link mw.log}
+ * @method
*/
- log: function () { },
+ log: ( function () {
+ var log = function () {};
+ log.warn = function () {};
+ log.deprecate = function ( obj, key, val ) {
+ obj[key] = val;
+ };
+ return log;
+ }() ),
// Make the Map constructor publicly available.
Map: Map,
*/
libs: {},
- /* Extension points */
-
/**
* @property
*/
-/**
+/*!
* Logger for MediaWiki javascript.
* Implements the stub left by the main 'mediawiki' module.
*
( function ( mw, $ ) {
+ /**
+ * @class mw.log
+ * @singleton
+ */
+
/**
* Logs a message to the console.
*
* In the case the browser does not have a console API, a console is created on-the-fly by appending
- * a <div id="mw-log-console"> element to the bottom of the body and then appending this and future
+ * a `<div id="mw-log-console">` element to the bottom of the body and then appending this and future
* messages to that, instead of the console.
*
- * @param {String} First in list of variadic messages to output to console.
+ * @param {string...} msg Messages to output to console.
*/
- mw.log = function ( /* logmsg, logmsg, */ ) {
+ mw.log = function () {
// Turn arguments into an array
var args = Array.prototype.slice.call( arguments ),
// Allow log messages to use a configured prefix to identify the source window (ie. frame)
hovzer.update();
}
$log.append(
- $( '<div></div>' )
+ $( '<div>' )
.css( {
borderBottom: 'solid 1px #DDDDDD',
fontSize: 'small',
} );
};
+ /**
+ * Write a message the console's warning channel.
+ * Also logs a stacktrace for easier debugging.
+ * Each action is silently ignored if the browser doesn't support it.
+ *
+ * @param {string...} msg Messages to output to console
+ */
+ mw.log.warn = function () {
+ var console = window.console;
+ if ( console && console.warn ) {
+ console.warn.apply( console, arguments );
+ if ( console.trace ) {
+ console.trace();
+ }
+ }
+ };
+
+ /**
+ * Create a property in a host object that, when accessed, will produce
+ * a deprecation warning in the console with backtrace.
+ *
+ * @param {Object} obj Host object of deprecated property
+ * @param {string} key Name of property to create in `obj`
+ * @param {Mixed} val The value this property should return when accessed
+ * @param {string} [msg] Optional text to include in the deprecation message.
+ */
+ mw.log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
+ obj[key] = val;
+ } : function ( obj, key, val, msg ) {
+ msg = 'MWDeprecationWarning: Use of "' + key + '" property is deprecated.' +
+ ( msg ? ( ' ' + msg ) : '' );
+ try {
+ Object.defineProperty( obj, key, {
+ configurable: true,
+ enumerable: true,
+ get: function () {
+ mw.log.warn( msg );
+ return val;
+ },
+ set: function ( newVal ) {
+ mw.log.warn( msg );
+ val = newVal;
+ }
+ } );
+ } catch ( err ) {
+ // IE8 can throw on Object.defineProperty
+ obj[key] = val;
+ }
+ };
+
}( mediaWiki, jQuery ) );
}
/* preference page with js-genrated toc */
-#mw-pref-clear {
- clear: both;
-}
#preftoc {
float: left;
margin: 1em 1em 1em 1em;
float: left;
margin-bottom: 2em;
color: #000;
+ font-weight: bold;
}
.errorbox {
border-color: red;
return s;
};
-// Special stylesheet links for Monobook only (see bug 14717)
-var skinpath = mw.config.get( 'stylepath' ) + '/' + mw.config.get( 'skin' );
-if ( mw.config.get( 'skin' ) === 'monobook' ) {
- if ( opera6_bugs ) {
- importStylesheetURI( skinpath + '/Opera6Fixes.css' );
- } else if ( opera7_bugs ) {
- importStylesheetURI( skinpath + '/Opera7Fixes.css' );
- } else if ( opera95_bugs ) {
- importStylesheetURI( skinpath + '/Opera9Fixes.css' );
- }
-}
-
if ( mw.config.get( 'wgBreakFrames' ) ) {
// Un-trap us from framesets
if ( window.top != window ) {
text-decoration: none;
color: white;
}
-
-#mw-pref-clear {
- clear: both;
-}
-
#mw_content a.external,
#mw_content a.external[href ^="gopher://"] {
/* @embed */
+++ /dev/null
-/* opera 6 fixes */
-div#column-one {
- position: relative;
- max-width: 11.7em;
-}
-#p-personal {
- width: 45em;
- margin-left: 8.6em;
- right: 0;
-}
-#bodyContent a.external {
- background: url(external.png) center right no-repeat;
- padding-right: 13px;
-}
-
-.rtl a.feedlink {
- background-position: right;
- padding-right: 0;
- padding-left: 16px;
-}
+++ /dev/null
-/* small tweaks for opera seven */
-#p-cactions {
- margin-top: .1em;
-}
-#p-cactions li a {
- top: 2px;
-}
-#bodyContent a.external {
- background: url(external.png) center right no-repeat;
- padding-right: 13px;
-}
-.rtl #bodyContent a.external {
- background-image: url(external-rtl.png);
- padding-right: 13px;
-}
-
-.rtl a.feedlink {
- background-position: right;
- padding-right: 0;
- padding-left: 16px;
-}
+++ /dev/null
-.rtl #bodyContent a.external {
- background-image: url(external-rtl.png);
- padding-right: 13px;
- padding-left: 0;
-}
-
-.rtl a.feedlink {
- background-position: right;
- padding-right: 0;
- padding-left: 16px;
-}
$collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
} );
+ QUnit.test( 'premade toggler - options.linksPassthru' , 2, function ( assert ) {
+ var $collapsible, $content;
+
+ $collapsible = prepareCollapsible(
+ '<div class="mw-collapsible">' +
+ '<div class="mw-collapsible-toggle">' +
+ 'Toggle <a href="#top">toggle</a> toggle <b>toggle</b>' +
+ '</div>' +
+ '<div class="mw-collapsible-content">' + loremIpsum + '</div>' +
+ '</div>',
+ // Can't do asynchronous because we're testing that the event *doesn't* happen
+ { instantHide: true }
+ );
+ $content = $collapsible.find( '.mw-collapsible-content' );
+
+ $collapsible.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
+ assert.assertTrue( $content.is( ':visible' ), 'click event on link inside toggle passes through (content not toggled)' );
+
+ $collapsible.find( '.mw-collapsible-toggle b' ).trigger( 'click' );
+ assert.assertTrue( $content.is( ':hidden' ), 'click event on non-link inside toggle toggles content' );
+ } );
+
}( mediaWiki, jQuery ) );