=== Bug fixes in 1.33 ===
* (T164211) Special:UserRights could sometimes fail with a
"conflict detected" error when there weren't any conflicts.
+* (T215566) Unable to determine if the database exists
+ during a fresh installation.
=== Action API changes in 1.33 ===
* (T198913) Added 'ApiOptions' hook.
"description": "MediaWiki extension.json schema",
"type": "object",
"additionalProperties": false,
+ "patternProperties": {
+ "^@": {
+ "type": "string",
+ "description": "Arbitrary notes, ignored by the parser."
+ }
+ },
"properties": {
"manifest_version": {
"type": "integer",
$this->lastdate = '';
$this->rclistOpen = false;
$this->getOutput()->addModuleStyles( [
+ 'mediawiki.icon',
'mediawiki.special.changeslist',
'mediawiki.special.changeslist.enhanced',
] );
$this->getOutput()->addModules( [
'jquery.makeCollapsible',
- 'mediawiki.icon',
] );
return '<div class="mw-changeslist">';
use Wikimedia\Rdbms\LBFactorySingle;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\DBExpectedError;
+use Wikimedia\Rdbms\DBConnectionError;
/**
* Base class for DBMS-specific installation helper classes.
return false;
}
- if ( !$this->db->selectDB( $this->getVar( 'wgDBname' ) ) ) {
+ try {
+ $this->db->selectDB( $this->getVar( 'wgDBname' ) );
+ } catch ( DBConnectionError $e ) {
+ // Don't catch DBConnectionError
+ throw $e;
+ } catch ( DBExpectedError $e ) {
return false;
}
}
}
- // A deleted key with a negative TTL left must be tombstoned
+ // Only a tombstoned key yields no value yet has a (negative) "current time left"
$isTombstone = ( $curTTL !== null && $value === false );
- if ( $isTombstone && $lockTSE <= 0 ) {
- // Use the INTERIM value for tombstoned keys to reduce regeneration load
- $lockTSE = self::INTERIM_KEY_TTL;
+ // Decide if only one thread should handle regeneration at a time
+ if ( $isTombstone ) {
+ // Note that since tombstones no-op set(), $lockTSE and $curTTL cannot be used to
+ // deduce the key hotness because $curTTL will always keep increasing until the
+ // tombstone expires or is overwritten by a new tombstone. Also, even if $lockTSE
+ // is not set, constant regeneration of a key for the tombstone lifetime might be
+ // very expensive. In either case, reduce regeneration load during this time by
+ // using the INTERIM value key with a small TTL.
+ $useMutex = true;
+ } else {
+ $useMutex =
+ // Assume a key is hot if requested soon ($lockTSE seconds) after invalidation.
+ // This avoids stampedes when timestamps from $checkKeys/$touchedCallback bump.
+ ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE ) ||
+ // Assume a key is hot if there is no value and a busy fallback is given.
+ // This avoids stampedes on eviction or preemptive renegeration taking too long.
+ ( $busyValue !== null && $value === false );
}
- // Assume a key is hot if requested soon after invalidation
- $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
- // Use the mutex if there is no value and a busy fallback is given
- $checkBusy = ( $busyValue !== null && $value === false );
- // Decide whether a single thread should handle regenerations.
- // This avoids stampedes when $checkKeys are bumped and when preemptive
- // renegerations take too long. It also reduces regenerations while $key
- // is tombstoned. This balances cache freshness with avoiding DB load.
- $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) || $checkBusy );
$lockAcquired = false;
if ( $useMutex ) {
$valueIsCacheable = ( $value !== false && $ttl >= 0 );
// When delete() is called, writes are write-holed by the tombstone,
- // so use a special INTERIM key to pass the new value around threads.
- if ( ( $isTombstone && $lockTSE > 0 ) && $valueIsCacheable ) {
- $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
+ // so use a special INTERIM key to pass the new value among threads.
+ if ( $isTombstone && $valueIsCacheable ) {
+ $tempTTL = max( self::INTERIM_KEY_TTL, (int)$lockTSE ); // set() expects seconds
$newAsOf = $this->getCurrentTime();
$wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
// Avoid using set() to avoid pointless mcrouter broadcasting
$this->setInterimValue( $key, $wrapped, $tempTTL );
}
- // Save the value unless a mutex-winning thread is already expected to do that
- if ( $valueIsCacheable && ( !$useMutex || $lockAcquired ) ) {
+ // Save the value unless a lock-winning thread is already expected to do that
+ if ( $valueIsCacheable && !$isTombstone && ( !$useMutex || $lockAcquired ) ) {
$setOpts['lockTSE'] = $lockTSE;
$setOpts['staleTTL'] = $staleTTL;
// Use best known "since" timestamp if not provided
// TODO replace with clear_last_error when requirements are bumped to PHP7
set_error_handler( function () {
}, 0 );
- \MediaWiki\suppressWarnings();
+ \Wikimedia\suppressWarnings();
trigger_error( '' );
- \MediaWiki\restoreWarnings();
+ \Wikimedia\restoreWarnings();
restore_error_handler();
$readPipes = array_filter( $pipes, function ( $fd ) use ( $desc ) {
<div class="mw-rcfilters-ui-highlights-color-c5" data-color="c5"></div>
</div>
</td>
- <td><span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span></td>
+ <td><span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space mw-collapsible-toggle-collapsed"></span></td>
<td class="mw-changeslist-line-prefix">{{{ prefix }}}</td>
<td class="mw-enhanced-rc" colspan="2">{{{ collectedRcFlags }}} {{ timestamp }} </td>
<td class="mw-changeslist-line-inner">
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.special.changeslist.enhanced' => [
- 'styles' => 'resources/src/mediawiki.special.changeslist.enhanced.css',
+ 'styles' => 'resources/src/mediawiki.special.changeslist.enhanced.less',
],
'mediawiki.special.changeslist.legend' => [
'styles' => 'resources/src/mediawiki.special.changeslist.legend.less',
'dependencies' => [
'mediawiki.api',
'mediawiki.jqueryMsg',
+ 'mediawiki.notify',
'mediawiki.Title',
'mediawiki.util',
'oojs-ui-core',
ul.mw-collapsible:not( @{exclude} ):before,
// Where the tbody or thead is the first child of the collapsible table
table.mw-collapsible:not( @{exclude} ) :first-child tr:first-child th:last-child:before,
- table.mw-collapsible:not( @{exclude} ) > caption:first-child:after {
+ table.mw-collapsible:not( @{exclude} ) > caption:first-child:after,
+ div.mw-collapsible:not( @{exclude} ):before {
content: '[@{msg-collapsible-collapse}]';
}
+++ /dev/null
-/*!
- * Styling for Special:Watchlist and Special:RecentChanges when preference 'usenewrc'
- * a.k.a. Enhanced Recent Changes is enabled.
- */
-
-table.mw-enhanced-rc {
- border: 0;
- border-spacing: 0;
-}
-
-table.mw-enhanced-rc th,
-table.mw-enhanced-rc td {
- padding: 0;
- vertical-align: top;
-}
-
-td.mw-enhanced-rc {
- white-space: nowrap;
- font-family: monospace, monospace;
-}
-
-.mw-enhanced-rc-time {
- font-family: monospace, monospace;
-}
-
-table.mw-enhanced-rc td.mw-enhanced-rc-nested {
- padding-left: 1em;
-}
-
-/* Show/hide arrows in enhanced changeslist */
-.mw-enhanced-rc .collapsible-expander {
- float: none;
-}
-
-/* If JS is disabled, the arrows or the placeholder space shouldn't be shown */
-.client-nojs .mw-enhancedchanges-arrow-space {
- display: none;
-}
-
-.mw-enhancedchanges-arrow {
- padding-top: 2px;
-}
-
-.mw-enhancedchanges-arrow-space {
- display: inline-block;
- *display: inline; /* IE7 and below */
- zoom: 1;
- width: 15px;
- height: 15px;
-}
-
-.mw-enhanced-watched .mw-enhanced-rc-time {
- font-weight: bold;
-}
-
-span.changedby {
- font-size: 95%;
-}
--- /dev/null
+/*!
+ * Styling for Special:Watchlist and Special:RecentChanges when preference 'usenewrc'
+ * a.k.a. Enhanced Recent Changes is enabled.
+ */
+
+table.mw-enhanced-rc {
+ border: 0;
+ border-spacing: 0;
+
+ th,
+ td {
+ padding: 0;
+ vertical-align: top;
+ }
+
+ td.mw-enhanced-rc-nested {
+ padding-left: 1em;
+ }
+}
+
+td.mw-enhanced-rc {
+ white-space: nowrap;
+ font-family: monospace, monospace;
+}
+
+.mw-enhanced-rc-time {
+ font-family: monospace, monospace;
+}
+
+/* Show/hide arrows in enhanced changeslist */
+.mw-enhanced-rc .collapsible-expander {
+ float: none;
+}
+
+/* If JS is disabled, the arrows or the placeholder space shouldn't be shown */
+.client-nojs .mw-enhancedchanges-arrow-space {
+ display: none;
+}
+
+.mw-enhancedchanges-arrow {
+ padding-top: 2px;
+}
+
+.mw-enhancedchanges-arrow-space {
+ display: inline-block;
+ *display: inline; /* IE7 and below */
+ zoom: 1;
+ width: 15px;
+ height: 15px;
+}
+
+.mw-enhanced-watched .mw-enhanced-rc-time {
+ font-weight: bold;
+}
+
+span.changedby {
+ font-size: 95%;
+}
* @param {Function} [callback] Callback to run after request resolution
*/
function addScript( src, callback ) {
+ // Use a <script> element rather than XHR. Using XHR changes the request
+ // headers (potentially missing a cache hit), and reduces caching in general
+ // since browsers cache XHR much less (if at all). And XHR means we retrieve
+ // text, so we'd need to eval, which then messes up line numbers.
+ // The drawback is that <script> does not offer progress events, feedback is
+ // only given after downloading, parsing, and execution have completed.
var script = document.createElement( 'script' );
script.src = src;
script.onload = script.onerror = function () {
{
"name": "FooBar",
+ "@note": "This is a note",
+ "@duck": "Docs say any @-item is ignored",
"attributes": {
"FooBar": {
"Attr": [ "test" ]
"Attr": [ "test2" ]
}
},
+ "config": {
+ "MyConfigValue": {
+ "value": 42,
+ "description": "Very important config value",
+ "public": true
+ }
+ },
"manifest_version": 2
}
$styleModules = $enhancedChangesList->getOutput()->getModuleStyles();
+ $this->assertContains(
+ 'mediawiki.icon',
+ $styleModules,
+ 'has mediawiki.icon'
+ );
+
$this->assertContains(
'mediawiki.special.changeslist',
$styleModules,
$modules = $enhancedChangesList->getOutput()->getModules();
$this->assertContains( 'jquery.makeCollapsible', $modules, 'has jquery.makeCollapsible' );
- $this->assertContains( 'mediawiki.icon', $modules, 'has mediawiki.icon' );
}
public function testBeginRecentChangesList_html() {