* Added HTMLAutoCompleteSelectField.
* Added a new hook, "SkinPreloadExistence", to allow extensions to add titles to
link existence cache before the page is rendered.
+* Config::set() was moved to its own interface, MutableConfig. GlobalVarConfig::set()
+ is now deprecated, does not implement MutableConfig.
+* A MutableConfig named HashConfig was added, that stores an array of configuration
+ settings.
+* (bug 69418) A MultiConfig implementation was added that supports fallback
+ to multiple Config instances.
=== Bug fixes in 1.24 ===
* (bug 50572) MediaWiki:Blockip should support gender
deprecated in favor of cmstarthexsortkey and cmendhexsortkey.
* (bug 63326) Add blockedtimestamp field to output of blockinfo property for
the list=allusers and list=users modules.
+* prop=imageinfo no longer requires iiurlwidth to be set when using iiurlparam.
=== Action API internal changes in 1.24 ===
* Methods for handling continuation are added to ApiResult, so actions other
$scale = array();
$scale['height'] = $params['urlheight'];
} else {
- $scale = null;
if ( $params['urlparam'] ) {
- $this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" );
+ // Audio files might not have a width/height.
+ $scale = array();
+ } else {
+ $scale = null;
}
}
* @return array Array of parameters for transform.
*/
protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
+ if ( $thumbParams === null ) {
+ // No scaling requested
+ return null;
+ }
if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
// We want to limit only by height in this situation, so pass the
// image's full width as the limiting width. But some file types
}
if ( !$otherParams ) {
+ $this->checkParameterNormalise( $image, $thumbParams );
return $thumbParams;
}
$p = $this->getModulePrefix();
// handlers.
$this->setWarning( "Could not parse {$p}urlparam for " . $image->getName()
. '. Using only width and height' );
-
+ $this->checkParameterNormalise( $image, $thumbParams );
return $thumbParams;
}
- if ( isset( $paramList['width'] ) ) {
+ if ( isset( $paramList['width'] ) && isset( $thumbParams['width'] ) ) {
if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
$this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
. "in favor of width value derived from {$p}urlwidth/{$p}urlheight "
}
}
- return $thumbParams + $paramList;
+ $finalParams = $thumbParams + $paramList;
+ $this->checkParameterNormalise( $image, $finalParams );
+ return $finalParams;
+ }
+
+ /**
+ * Verify that the final image parameters can be normalised.
+ *
+ * This doesn't use the normalised parameters, since $file->transform
+ * expects the pre-normalised parameters, but doing the normalisation
+ * allows us to catch certain error conditions early (such as missing
+ * required parameter).
+ *
+ * @param $image File
+ * @param $finalParams array List of parameters to transform image with
+ */
+ protected function checkParameterNormalise( $image, $finalParams ) {
+ $h = $image->getHandler();
+ if ( !$h ) {
+ return;
+ }
+ // Note: normaliseParams modifies the array in place, but we aren't interested
+ // in the actual normalised version, only if we can actually normalise them,
+ // so we use the functions scope to throw away the normalisations.
+ if ( !$h->normaliseParams( $image, $finalParams ) ) {
+ $this->dieUsage( "Could not normalise image parameters for " . $image->getName(), "urlparamnormal" );
+ }
}
/**
if ( $pageCount !== false ) {
$vals['pagecount'] = $pageCount;
}
+
+ // length as in how many seconds long a video is.
+ $length = $file->getLength();
+ if ( $length ) {
+ // Call it duration, because "length" can be ambiguous.
+ $vals['duration'] = (float)$length;
+ }
}
$pcomment = isset( $prop['parsedcomment'] );
'parsedcomment' => ' parsedcomment - Parse the comment on the version',
'canonicaltitle' => ' canonicaltitle - Adds the canonical title of the image file',
'url' => ' url - Gives URL to the image and the description page',
- 'size' => ' size - Adds the size of the image in bytes ' .
- 'and the height, width and page count (if applicable)',
+ 'size' => ' size - Adds the size of the image in bytes, ' .
+ 'its height and its width. Page count and duration are added if applicable',
'dimensions' => ' dimensions - Alias for size', // B/C with Allimages
'sha1' => ' sha1 - Adds SHA-1 hash for the image',
'mime' => ' mime - Adds MIME type of the image',
'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.'
),
'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" ),
+ 'urlparam' => array(
+ "A handler specific parameter string. For example, pdf's ",
+ "might use 'page15-100px'."
+ ),
'limit' => 'How many image revisions to return per image',
'start' => 'Timestamp to start listing from',
'end' => 'Timestamp to stop listing at',
if ( isset( $option['pass'] ) ) {
$this->setVar( '_AdminPassword', $option['pass'] );
}
+
+ // Set up the default skins
+ $skins = $this->findExtensions( 'skins' );
+ $this->setVar( '_Skins', $skins );
+
+ if ( $skins ) {
+ $skinNames = array_map( 'strtolower', $skins );
+ $this->setVar( 'wgDefaultSkin', $this->getDefaultSkin( $skinNames ) );
+ }
}
/**
return $exts;
}
+ /**
+ * Returns a default value to be used for $wgDefaultSkin: the preferred skin, if available among
+ * the installed skins, or any other one otherwise.
+ *
+ * @param string[] $skinNames Names of installed skins.
+ * @return string
+ */
+ public function getDefaultSkin( array $skinNames ) {
+ $defaultSkin = $GLOBALS['wgDefaultSkin'];
+ if ( in_array( $defaultSkin, $skinNames ) ) {
+ return $defaultSkin;
+ } else {
+ return $skinNames[0];
+ }
+ }
+
/**
* Installs the auto-detected extensions.
*
'var' => 'wgDefaultSkin',
'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
'values' => $skinNames,
- 'value' => $this->getVar( 'wgDefaultSkin', $this->getDefaultSkin( $skinNames ) ),
+ 'value' => $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) ),
) );
foreach ( $skins as $skin ) {
$this->addHTML( $this->getCCDoneBox() );
}
- /**
- * Returns a default value to be used for $wgDefaultSkin: the preferred skin, if available among
- * the installed skins, or any other one otherwise.
- *
- * @param string[] $skinNames Names of installed skins.
- * @return string
- */
- public function getDefaultSkin( array $skinNames ) {
- $defaultSkin = $GLOBALS['wgDefaultSkin'];
- if ( in_array( $defaultSkin, $skinNames ) ) {
- return $defaultSkin;
- } else {
- return $skinNames[0];
- }
- }
-
/**
* If the user skips this installer page, we still need to set up the default skins, but ignore
* everything else.
if ( $skins ) {
$skinNames = array_map( 'strtolower', $skins );
- $this->parent->setVar( 'wgDefaultSkin', $this->getDefaultSkin( $skinNames ) );
+ $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
}
return true;
*/
protected $editor;
+ /**
+ * @param User $editor The editor that triggered the update. Their notification
+ * timestamp will not be updated(they have already seen it)
+ * @param Title $title The title to update timestamps for
+ * @param string $timestamp Set the upate timestamp to this value
+ * @return int[]
+ */
+ public static function updateWatchlistTimestamp( User $editor, Title $title, $timestamp ) {
+ global $wgEnotifWatchlist, $wgShowUpdatedMarker;
+
+ if ( !$wgEnotifWatchlist && !$wgShowUpdatedMarker ) {
+ return array();
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select( array( 'watchlist' ),
+ array( 'wl_user' ),
+ array(
+ 'wl_user != ' . intval( $editor->getID() ),
+ 'wl_namespace' => $title->getNamespace(),
+ 'wl_title' => $title->getDBkey(),
+ 'wl_notificationtimestamp IS NULL',
+ ), __METHOD__
+ );
+
+ $watchers = array();
+ foreach ( $res as $row ) {
+ $watchers[] = intval( $row->wl_user );
+ }
+
+ if ( $watchers ) {
+ // Update wl_notificationtimestamp for all watching users except the editor
+ $fname = __METHOD__;
+ $dbw->onTransactionIdle(
+ function () use ( $dbw, $timestamp, $watchers, $title, $fname ) {
+ $dbw->update( 'watchlist',
+ array( /* SET */
+ 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp )
+ ), array( /* WHERE */
+ 'wl_user' => $watchers,
+ 'wl_namespace' => $title->getNamespace(),
+ 'wl_title' => $title->getDBkey(),
+ ), $fname
+ );
+ }
+ );
+ }
+
+ return $watchers;
+ }
+
/**
* Send emails corresponding to the user $editor editing the page $title.
* Also updates wl_notificationtimestamp.
public function notifyOnPageChange( $editor, $title, $timestamp, $summary,
$minorEdit, $oldid = false, $pageStatus = 'changed'
) {
- global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifMinorEdits,
- $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk;
+ global $wgEnotifUseJobQ, $wgEnotifMinorEdits, $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk;
if ( $title->getNamespace() < 0 ) {
return;
}
- // Build a list of users to notify
- $watchers = array();
- if ( $wgEnotifWatchlist || $wgShowUpdatedMarker ) {
- $dbw = wfGetDB( DB_MASTER );
- $res = $dbw->select( array( 'watchlist' ),
- array( 'wl_user' ),
- array(
- 'wl_user != ' . intval( $editor->getID() ),
- 'wl_namespace' => $title->getNamespace(),
- 'wl_title' => $title->getDBkey(),
- 'wl_notificationtimestamp IS NULL',
- ), __METHOD__
- );
- foreach ( $res as $row ) {
- $watchers[] = intval( $row->wl_user );
- }
- if ( $watchers ) {
- // Update wl_notificationtimestamp for all watching users except the editor
- $fname = __METHOD__;
- $dbw->onTransactionIdle(
- function () use ( $dbw, $timestamp, $watchers, $title, $fname ) {
- $dbw->update( 'watchlist',
- array( /* SET */
- 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp )
- ), array( /* WHERE */
- 'wl_user' => $watchers,
- 'wl_namespace' => $title->getNamespace(),
- 'wl_title' => $title->getDBkey(),
- ), $fname
- );
- }
- );
- }
- }
+ // update wl_notificationtimestamp for watchers
+ $watchers = self::updateWatchlistTimestamp( $editor, $title, $timestamp );
$sendEmail = true;
// If nobody is watching the page, and there are no users notified on all changes
'mediawiki.page.ready' => array(
'scripts' => 'resources/src/mediawiki.page/mediawiki.page.ready.js',
'dependencies' => array(
+ 'jquery.accessKeyLabel',
'jquery.checkboxShiftClick',
'jquery.makeCollapsible',
'jquery.placeholder',
'jquery.mw-jump',
- 'mediawiki.util',
),
'targets' => array( 'desktop', 'mobile' ),
),
/**
* Misc. utilities
*
- * @deprecated since 1.17 Use mediawiki.util instead
+ * @deprecated since 1.17 Use mediawiki.util or jquery.accessKeyLabel instead
*/
msg = 'Use mediawiki.util instead.';
-mw.log.deprecate( win, 'updateTooltipAccessKeys', mw.util.updateTooltipAccessKeys, msg );
mw.log.deprecate( win, 'addPortletLink', mw.util.addPortletLink, msg );
mw.log.deprecate( win, 'appendCSS', mw.util.addCSS, msg );
msg = 'Use jquery.accessKeyLabel instead.';
mw.log.deprecate( win, 'tooltipAccessKeyPrefix', 'alt-', msg );
mw.log.deprecate( win, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, msg );
+// mw.util.updateTooltipAccessKeys already generates a deprecation message.
+win.updateTooltipAccessKeys = function () {
+ return mw.util.updateTooltipAccessKeys.apply( null, arguments );
+};
/**
* Wikipage import methods
// Things outside the wikipage content
$( function () {
+ var $nodes;
if ( !supportsPlaceholder ) {
// Exclude content to avoid hitting it twice for the (first) wikipage content
}
// Add accesskey hints to the tooltips
- mw.util.updateTooltipAccessKeys();
+ if ( document.querySelectorAll ) {
+ // If we're running on a browser where we can do this efficiently,
+ // just find all elements that have accesskeys. We can't use jQuery's
+ // polyfill for the selector since looping over all elements on page
+ // load might be too slow.
+ $nodes = $( document.querySelectorAll( '[accesskey]' ) );
+ } else {
+ // Otherwise go through some elements likely to have accesskeys rather
+ // than looping over all of them. Unfortunately this will not fully
+ // work for custom skins with different HTML structures. Input, label
+ // and button should be rare enough that no optimizations are needed.
+ $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label, button' );
+ }
+ $nodes.updateTooltipAccessKeys();
} );
border: 1px solid @colorFieldBorder;
.box-sizing(border-box);
width: 100%;
- padding: .3em .3em .3em .6em;
+ padding: .4em .3em .2em .6em;
display: block;
vertical-align: middle;
border-radius: @borderRadius;
return null;
},
- /**
- * Add the appropriate prefix to the accesskey shown in the tooltip.
- *
- * If the `$nodes` parameter is given, only those nodes are updated;
- * otherwise, depending on browser support, we update either all elements
- * with accesskeys on the page or a bunch of elements which are likely to
- * have them on core skins.
- *
- * @param {Array|jQuery} [$nodes] A jQuery object, or array of nodes to update.
- */
- updateTooltipAccessKeys: function ( $nodes ) {
- if ( !$nodes ) {
- if ( document.querySelectorAll ) {
- // If we're running on a browser where we can do this efficiently,
- // just find all elements that have accesskeys. We can't use jQuery's
- // polyfill for the selector since looping over all elements on page
- // load might be too slow.
- $nodes = $( document.querySelectorAll( '[accesskey]' ) );
- } else {
- // Otherwise go through some elements likely to have accesskeys rather
- // than looping over all of them. Unfortunately this will not fully
- // work for custom skins with different HTML structures. Input, label
- // and button should be rare enough that no optimizations are needed.
- $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label, button' );
- }
- } else if ( !( $nodes instanceof $ ) ) {
- $nodes = $( $nodes );
- }
-
- $nodes.updateTooltipAccessKeys();
- },
-
/**
* The content wrapper of the skin (e.g. `.mw-body`).
*
*/
mw.log.deprecate( util, 'tooltipAccessKeyRegexp', /\[(ctrl-)?(option-)?(alt-)?(shift-)?(esc-)?(.)\]$/, 'Use jquery.accessKeyLabel instead.' );
+ /**
+ * Add the appropriate prefix to the accesskey shown in the tooltip.
+ *
+ * If the `$nodes` parameter is given, only those nodes are updated;
+ * otherwise, depending on browser support, we update either all elements
+ * with accesskeys on the page or a bunch of elements which are likely to
+ * have them on core skins.
+ *
+ * @method updateTooltipAccessKeys
+ * @param {Array|jQuery} [$nodes] A jQuery object, or array of nodes to update.
+ * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
+ */
+ mw.log.deprecate( util, 'updateTooltipAccessKeys', function ( $nodes ) {
+ if ( !$nodes ) {
+ if ( document.querySelectorAll ) {
+ // If we're running on a browser where we can do this efficiently,
+ // just find all elements that have accesskeys. We can't use jQuery's
+ // polyfill for the selector since looping over all elements on page
+ // load might be too slow.
+ $nodes = $( document.querySelectorAll( '[accesskey]' ) );
+ } else {
+ // Otherwise go through some elements likely to have accesskeys rather
+ // than looping over all of them. Unfortunately this will not fully
+ // work for custom skins with different HTML structures. Input, label
+ // and button should be rare enough that no optimizations are needed.
+ $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label, button' );
+ }
+ } else if ( !( $nodes instanceof $ ) ) {
+ $nodes = $( $nodes );
+ }
+
+ $nodes.updateTooltipAccessKeys();
+ }, 'Use jquery.accessKeyLabel instead.' );
+
/**
* Add a little box at the top of the screen to inform the user of
* something, replacing any previous message.