Those wishing to use the latest code instead of a branch release can [[Download
from SVN|obtain it from source control]].
+=== What's new in 1.6 ===
+
+'''User interface:'''
+* The account creation form has been separated from the user login form.
+* Page protection/unprotection uses a new, expanded form
+
+'''Templates:'''
+* Categories and "what links here" now update as expected when adding or
+removing links in a template.
+* Template parameters can now have default values, as <nowiki>{{{name|default
+value}}}</nowiki>
+
+'''Uploads:'''
+* Optional support for rasterizing SVG images to PNG for inline display
+
+'''Feeds:'''
+* Feed generation upgraded to Atom 1.0
+* Diffs in RSS and Atom feeds are now colored for improved readability.
+
+'''Database:'''
+* MySQL 3.23.x support dropped; 4.0 or later required
+* Experimental support for Unicode mode of MySQL 4.1/5.0 (moderately tested)
+* Experimental Oracle support (not well tested!)
+
+'''Anti-spam extension support:'''
+* [[meta:SpamBlacklist extension|SpamBlacklist extension]] now has support for
+automated cleanup.
+* Support for a [[meta:ConfirmEdit extension|captcha extension]] to restrict
+automated spam edits.
+
+Numerous bug fixes and other behind-the-scenes changes have been made; see the
+file HISTORY for a complete change list.
+
== Changes since 1.5 ==
* (bug 2885) More PHP 5.1 fixes: skin, search, log, undelete
* (bug 2139) Show page title in subtitle when viewing "read only" page
* (bug 5452) Update language name for Cree
-=== What's new in 1.6 ===
-
-'''User interface:'''
-* The account creation form has been separated from the user login form.
-* Page protection/unprotection uses a new, expanded form
-
-'''Templates:'''
-* Categories and "what links here" now update as expected when adding or
-removing links in a template.
-* Template parameters can now have default values, as <nowiki>{{{name|default
-value}}}</nowiki>
-
-'''Uploads:'''
-* Optional support for rasterizing SVG images to PNG for inline display
-
-'''Feeds:'''
-* Feed generation upgraded to Atom 1.0
-* Diffs in RSS and Atom feeds are now colored for improved readability.
-
-'''Database:'''
-* MySQL 3.23.x support dropped; 4.0 or later required
-* Experimental support for Unicode mode of MySQL 4.1/5.0 (moderately tested)
-* Experimental Oracle support (not well tested!)
-
-'''Anti-spam extension support:'''
-* [[meta:SpamBlacklist extension|SpamBlacklist extension]] now has support for
-automated cleanup.
-* Support for a [[meta:ConfirmEdit extension|captcha extension]] to restrict
-automated spam edits.
-
-Numerous bug fixes and other behind-the-scenes changes have been made; see the
-file HISTORY for a complete change list.
-
== Compatibility ==
Older PHP 4.2 and 4.1 releases are no longer supported; PHP 4 users must
recommended on live sites. (This must be set for MathML to display properly in
Mozilla.)
-----
+
+= MediaWiki 1.5 =
+
+== MediaWiki 1.5.9 ==
+* (bug 3359) Add hooks on completion of file upload
== MediaWiki 1.5.8 ==
changed to explicitly cast. Subclasses relying on the base-class
implementation should check whether they need to override it now.
* BagOStuff::add is now abstract and must explicitly be defined in subclasses.
+* LinksDeletionUpdate is now a subclass of LinksUpdate. As a consequence,
+ the following hooks will now be triggered upon page deletion in addition
+ to page updates: LinksUpdateConstructed, LinksUpdate, LinksUpdateComplete.
+ LinksUpdateAfterInsert is not triggered since deletions do not cause
+ insertions into links tables.
== Compatibility ==
MediaWiki 1.33 requires PHP 7.0.13 or later. Although HHVM 3.18.5 or later is
*/
use MediaWiki\MediaWikiServices;
use Wikimedia\ScopedCallback;
-use Wikimedia\Rdbms\IDatabase;
/**
* Update object handling the cleanup of links tables after a page was deleted.
*/
-class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
+class LinksDeletionUpdate extends LinksUpdate implements EnqueueableDataUpdate {
/** @var WikiPage */
protected $page;
- /** @var int */
- protected $pageId;
/** @var string */
protected $timestamp;
- /** @var IDatabase */
- private $db;
-
/**
* @param WikiPage $page Page we are updating
* @param int|null $pageId ID of the page we are updating [optional]
* @throws MWException
*/
function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
- parent::__construct();
-
$this->page = $page;
if ( $pageId ) {
- $this->pageId = $pageId; // page ID at time of deletion
+ $this->mId = $pageId; // page ID at time of deletion
} elseif ( $page->exists() ) {
- $this->pageId = $page->getId();
+ $this->mId = $page->getId();
} else {
throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" );
}
$this->timestamp = $timestamp ?: wfTimestampNow();
+
+ $fakePO = new ParserOutput();
+ $fakePO->setCacheTime( $timestamp );
+ parent::__construct( $page->getTitle(), $fakePO, false );
}
- public function doUpdate() {
+ protected function doIncrementalUpdate() {
$services = MediaWikiServices::getInstance();
$config = $services->getMainConfig();
$lbFactory = $services->getDBLoadBalancerFactory();
$batchSize = $config->get( 'UpdateRowsPerQuery' );
- // Page may already be deleted, so don't just getId()
- $id = $this->pageId;
-
- if ( $this->ticket ) {
- // Make sure all links update threads see the changes of each other.
- // This handles the case when updates have to batched into several COMMITs.
- $scopedLock = LinksUpdate::acquirePageLock( $this->getDB(), $id );
- if ( !$scopedLock ) {
- throw new RuntimeException( "Could not acquire lock for page ID '{$id}'." );
- }
- }
+ $id = $this->mId;
+ $title = $this->mTitle;
- $title = $this->page->getTitle();
$dbw = $this->getDB(); // convenience
- // Delete restrictions for it
- $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
+ parent::doIncrementalUpdate();
- // Fix category table counts
- $cats = $dbw->selectFieldValues(
- 'categorylinks',
- 'cl_to',
- [ 'cl_from' => $id ],
- __METHOD__
- );
- $catBatches = array_chunk( $cats, $batchSize );
- foreach ( $catBatches as $catBatch ) {
- $this->page->updateCategoryCounts( [], $catBatch, $id );
- if ( count( $catBatches ) > 1 ) {
- // Only sacrifice atomicity if necessary due to size
- $lbFactory->commitAndWaitForReplication(
- __METHOD__, $this->ticket, [ 'domain' => $dbw->getDomainID() ]
- );
- }
- }
-
- // Refresh counts on categories that should be empty now
+ // Typically, a category is empty when deleted, so check that we don't leave
+ // spurious row in the category table.
if ( $title->getNamespace() === NS_CATEGORY ) {
// T166757: do the update after the main job DB commit
DeferredUpdates::addCallableUpdate( function () use ( $title ) {
} );
}
- $this->batchDeleteByPK(
- 'pagelinks',
- [ 'pl_from' => $id ],
- [ 'pl_from', 'pl_namespace', 'pl_title' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'imagelinks',
- [ 'il_from' => $id ],
- [ 'il_from', 'il_to' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'categorylinks',
- [ 'cl_from' => $id ],
- [ 'cl_from', 'cl_to' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'templatelinks',
- [ 'tl_from' => $id ],
- [ 'tl_from', 'tl_namespace', 'tl_title' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'externallinks',
- [ 'el_from' => $id ],
- [ 'el_id' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'langlinks',
- [ 'll_from' => $id ],
- [ 'll_from', 'll_lang' ],
- $batchSize
- );
- $this->batchDeleteByPK(
- 'iwlinks',
- [ 'iwl_from' => $id ],
- [ 'iwl_from', 'iwl_prefix', 'iwl_title' ],
- $batchSize
- );
+ // Delete restrictions for the deleted page
+ $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
- // Delete any redirect entry or page props entries
+ // Delete any redirect entry
$dbw->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
- $dbw->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
// Find recentchanges entries to clean up...
$rcIdsForTitle = $dbw->selectFieldValues(
ScopedCallback::consume( $scopedLock );
}
- private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
- $services = MediaWikiServices::getInstance();
- $lbFactory = $services->getDBLoadBalancerFactory();
- $dbw = $this->getDB(); // convenience
-
- $res = $dbw->select( $table, $pk, $conds, __METHOD__ );
-
- $pkDeleteConds = [];
- foreach ( $res as $row ) {
- $pkDeleteConds[] = $dbw->makeList( (array)$row, LIST_AND );
- if ( count( $pkDeleteConds ) >= $bSize ) {
- $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
- $lbFactory->commitAndWaitForReplication(
- __METHOD__, $this->ticket, [ 'domain' => $dbw->getDomainID() ]
- );
- $pkDeleteConds = [];
- }
- }
-
- if ( $pkDeleteConds ) {
- $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
- }
- }
-
- protected function getDB() {
- if ( !$this->db ) {
- $this->db = wfGetDB( DB_MASTER );
- }
-
- return $this->db;
- }
-
public function getAsJobSpecification() {
return [
'domain' => $this->getDB()->getDomainID(),
'job' => new JobSpecification(
'deleteLinks',
- [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
+ [ 'pageId' => $this->mId, 'timestamp' => $this->timestamp ],
[ 'removeDuplicates' => true ],
- $this->page->getTitle()
+ $this->mTitle
)
];
}
parent::__construct();
$this->mTitle = $title;
- $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
+
+ if ( !$this->mId ) {
+ // NOTE: subclasses may initialize mId before calling this constructor!
+ $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
+ }
if ( !$this->mId ) {
throw new InvalidArgumentException(
/**
* @return IDatabase
*/
- private function getDB() {
+ protected function getDB() {
if ( !$this->db ) {
$this->db = wfGetDB( DB_MASTER );
}
}
}
- return new MediaWiki\Widget\DateTimeInputWidget( $params );
+ if ( $this->mType === 'date' ) {
+ return new MediaWiki\Widget\DateInputWidget( $params );
+ } else {
+ return new MediaWiki\Widget\DateTimeInputWidget( $params );
+ }
}
protected function getOOUIModules() {
- return [ 'mediawiki.widgets.datetime' ];
+ if ( $this->mType === 'date' ) {
+ return [ 'mediawiki.widgets.DateInputWidget' ];
+ } else {
+ return [ 'mediawiki.widgets.datetime' ];
+ }
}
protected function shouldInfuseOOUI() {
return $result;
}
- public function changeTTL( $key, $expiry = 0, $flags = 0 ) {
+ public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
return false;
}
- $expiry = $this->convertToRelative( $expiry );
+ $relative = $this->expiryIsRelative( $exptime );
try {
- $result = $conn->expire( $key, $expiry );
+ if ( $exptime == 0 ) {
+ $result = $conn->persist( $key );
+ $this->logRequest( 'persist', $key, $server, $result );
+ } elseif ( $relative ) {
+ $result = $conn->expire( $key, $this->convertToRelative( $exptime ) );
+ $this->logRequest( 'expire', $key, $server, $result );
+ } else {
+ $result = $conn->expireAt( $key, $this->convertToExpiry( $exptime ) );
+ $this->logRequest( 'expireAt', $key, $server, $result );
+ }
} catch ( RedisException $e ) {
$result = false;
$this->handleException( $conn, $e );
}
- $this->logRequest( 'expire', $key, $server, $result );
return $result;
}
* It is generally preferable to use a class constant when setting this value.
* This has no effect unless pcTTL is used.
* Default: WANObjectCache::PC_PRIMARY.
- * - version: Integer version number. This allows for callers to make breaking changes to
- * how values are stored while maintaining compatability and correct cache purges. New
- * versions are stored alongside older versions concurrently. Avoid storing class objects
- * however, as this reduces compatibility (due to serialization).
+ * - version: Integer version number. This lets callers make breaking changes to the format
+ * of cached values without causing problems for sites that use non-instantaneous code
+ * deployments. Old and new code will recognize incompatible versions and purges from
+ * both old and new code will been seen by each other. When this method encounters an
+ * incompatibly versioned value at the provided key, a "variant key" will be used for
+ * reading from and saving to cache. The variant key is specific to the key and version
+ * number provided to this method. If the variant key value is older than that of the
+ * provided key, or the provided key is non-existant, then the variant key will be seen
+ * as non-existant. Therefore, delete() calls invalidate the provided key's variant keys.
+ * The "checkKeys" and "touchedCallback" options still apply to variant keys as usual.
+ * Avoid storing class objects, as this reduces compatibility (due to serialization).
* Default: null.
* - minAsOf: Reject values if they were generated before this UNIX timestamp.
* This is useful if the source of a key is suspected of having possibly changed
}
protected function wrapFieldSetSection( $legend, $section, $attributes, $isRoot ) {
- // to get a user visible effect, wrap the fieldset into a framed panel layout
- if ( $isRoot ) {
- // Mimic TabPanelLayout
- $wrapper = new OOUI\PanelLayout( [
- 'expanded' => false,
- 'scrollable' => true,
- // Framed and padded for no-JS, frame hidden with CSS
- 'framed' => true,
- 'infusable' => false,
- 'classes' => [ 'oo-ui-stackLayout oo-ui-indexLayout-stackLayout' ]
- ] );
- $layout = new OOUI\PanelLayout( [
- 'expanded' => false,
- 'scrollable' => true,
- 'infusable' => false,
- 'classes' => [ 'oo-ui-tabPanelLayout' ]
- ] );
- $wrapper->appendContent( $layout );
- } else {
- $wrapper = $layout = new OOUI\PanelLayout( [
- 'expanded' => false,
- 'padded' => true,
- 'framed' => true,
- 'infusable' => false,
- ] );
- }
+ $layout = parent::wrapFieldSetSection( $legend, $section, $attributes, $isRoot );
- $layout->appendContent(
- new OOUI\FieldsetLayout( [
- 'label' => $legend,
- 'infusable' => false,
- 'items' => [
- new OOUI\Widget( [
- 'content' => new OOUI\HtmlSnippet( $section )
- ] ),
- ],
- ] + $attributes )
- );
- return $wrapper;
+ $layout->addClasses( [ 'mw-prefs-fieldset-wrapper' ] );
+ $layout->removeClasses( [ 'oo-ui-panelLayout-framed' ] );
+
+ return $layout;
}
/**
* @return string
*/
function getBody() {
- // Construct fake tabs to avoid FOUC. The structure mimics OOUI's tabPanelLayout.
- // TODO: Consider creating an infusable TabPanelLayout in OOUI-PHP.
- $fakeTabs = [];
- foreach ( $this->getPreferenceSections() as $i => $key ) {
- $fakeTabs[] =
- Html::rawElement(
- 'div',
- [
- 'class' =>
- 'oo-ui-widget oo-ui-widget-enabled oo-ui-optionWidget ' .
- 'oo-ui-tabOptionWidget oo-ui-labelElement' .
- ( $i === 0 ? ' oo-ui-optionWidget-selected' : '' )
+ $tabPanels = [];
+ foreach ( $this->mFieldTree as $key => $val ) {
+ if ( !is_array( $val ) ) {
+ wfDebug( __METHOD__ . " encountered a field not attached to a section: '$key'" );
+ continue;
+ }
+ $label = $this->getLegend( $key );
+ $content =
+ $this->getHeaderText( $key ) .
+ $this->displaySection( $this->mFieldTree[$key] ) .
+ $this->getFooterText( $key );
+
+ $tabPanels[] = new OOUI\TabPanelLayout( [
+ 'classes' => [ 'mw-htmlform-autoinfuse-lazy' ],
+ 'name' => 'mw-prefsection-' . $key,
+ 'label' => $label,
+ 'content' => new OOUI\FieldsetLayout( [
+ 'classes' => [ 'mw-prefs-section-fieldset' ],
+ 'label' => $label,
+ 'items' => [
+ new OOUI\Widget( [
+ 'content' => new OOUI\HtmlSnippet( $content )
+ ] ),
],
- Html::element(
- 'a',
- [
- 'class' => 'oo-ui-labelElement-label',
- // Make this a usable link instead of a span so the tabs
- // can be used before JS runs
- 'href' => '#mw-prefsection-' . $key
- ],
- $this->getLegend( $key )
- )
- );
+ ] ),
+ 'expanded' => false,
+ 'framed' => true,
+ ] );
}
- $fakeTabsHtml = Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-layout oo-ui-panelLayout oo-ui-indexLayout-tabPanel' ],
- Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-widget oo-ui-widget-enabled oo-ui-selectWidget ' .
- 'oo-ui-selectWidget-depressed oo-ui-tabSelectWidget' ],
- implode( $fakeTabs )
- )
- );
-
- return Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-layout oo-ui-panelLayout oo-ui-panelLayout-framed mw-prefs-faketabs' ],
- Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-layout oo-ui-menuLayout oo-ui-menuLayout-static ' .
- 'oo-ui-menuLayout-top oo-ui-menuLayout-showMenu oo-ui-indexLayout' ],
- Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-menuLayout-menu' ],
- $fakeTabsHtml
- ) .
- Html::rawElement(
- 'div',
- [ 'class' => 'oo-ui-menuLayout-content mw-htmlform-autoinfuse-lazy' ],
- $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' )
- )
- )
- );
+
+ $indexLayout = new OOUI\IndexLayout( [
+ 'infusable' => true,
+ 'expanded' => false,
+ 'autoFocus' => false,
+ 'classes' => [ 'mw-prefs-tabs' ],
+ ] );
+ $indexLayout->addTabPanels( $tabPanels );
+
+ return new OOUI\PanelLayout( [
+ 'framed' => true,
+ 'expanded' => false,
+ 'classes' => [ 'mw-prefs-tabs-wrapper' ],
+ 'content' => $indexLayout
+ ] );
}
/**
*/
( function () {
$( function () {
- var $preferences, tabs, wrapper, previousTab, switchingNoHash;
-
- $preferences = $( '#preferences' );
+ var tabs, previousTab, switchingNoHash;
// Make sure the accessibility tip is focussable so that keyboard users take notice,
// but hide it by default to reduce visual clutter.
} )
.insertBefore( '.mw-htmlform-ooui-wrapper' );
- tabs = new OO.ui.IndexLayout( {
- expanded: false,
- // Do not remove focus from the tabs menu after choosing a tab
- autoFocus: false
- } );
-
- mw.config.get( 'wgPreferencesTabs' ).forEach( function ( tabConfig ) {
- var panel, $panelContents;
-
- panel = new OO.ui.TabPanelLayout( tabConfig.name, {
- expanded: false,
- label: tabConfig.label
- } );
- $panelContents = $( '#mw-prefsection-' + tabConfig.name );
-
- // Hide the unnecessary PHP PanelLayouts
- // (Do not use .remove(), as that would remove event handlers for everything inside them)
- $panelContents.parent().detach();
+ tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) );
- panel.$element.append( $panelContents );
- tabs.addTabPanels( [ panel ] );
-
- // Remove duplicate labels
- // (This must be after .addTabPanels(), otherwise the tab item doesn't exist yet)
- $panelContents.children( 'legend' ).remove();
- $panelContents.attr( 'aria-labelledby', panel.getTabItem().getElementId() );
- } );
-
- wrapper = new OO.ui.PanelLayout( {
- expanded: false,
- padded: false,
- framed: true
- } );
- wrapper.$element.append( tabs.$element );
- $preferences.prepend( wrapper.$element );
- $( '.mw-prefs-faketabs' ).remove();
+ tabs.$element.addClass( 'mw-prefs-tabs-infused' );
function enhancePanel( panel ) {
if ( !panel.$element.data( 'mw-section-infused' ) ) {
- // mw-htmlform-autoinfuse-lazy class has been removed by replacing faketabs
+ panel.$element.removeClass( 'mw-htmlform-autoinfuse-lazy' );
mw.hook( 'htmlform.enhance' ).fire( panel.$element );
panel.$element.data( 'mw-section-infused', true );
}
// Changing the hash apparently causes keyboard focus to be lost?
// Save and restore it. This makes no sense though.
active = document.activeElement;
- location.hash = '#mw-prefsection-' + panel.getName();
+ location.hash = '#' + panel.getName();
if ( active ) {
active.focus();
}
/**
* @ignore
- * @param {string} name the name of a tab without the prefix ("mw-prefsection-")
+ * @param {string} name The name of a tab
* @param {boolean} [noHash] A hash will be set according to the current
* open section. Use this flag to suppress this.
*/
matchedElement, parentSection;
if ( hash.match( /^#mw-prefsection-[\w]+$/ ) ) {
mw.storage.session.remove( 'mwpreferences-prevTab' );
- switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
+ switchPrefTab( hash.slice( 1 ) );
} else if ( hash.match( /^#mw-[\w-]+$/ ) ) {
matchedElement = document.getElementById( hash.slice( 1 ) );
parentSection = $( matchedElement ).parent().closest( '[id^="mw-prefsection-"]' );
if ( parentSection.length ) {
mw.storage.session.remove( 'mwpreferences-prevTab' );
// Switch to proper tab and scroll to selected item.
- switchPrefTab( parentSection.attr( 'id' ).replace( 'mw-prefsection-', '' ), true );
+ switchPrefTab( parentSection.attr( 'id' ), true );
matchedElement.scrollIntoView();
}
}
if ( hash.match( /^#mw-[\w-]+/ ) ) {
detectHash();
} else if ( hash === '' ) {
- switchPrefTab( 'personal', true );
+ switchPrefTab( 'mw-prefsection-personal', true );
}
} )
// Run the function immediately to select the proper tab on startup.
overflow: hidden;
}
-/* Most outer Panellayout:
- * Decrease contrast of `border` slightly as padding/border combination is sufficient
- * accessibility wise and focus of content is more important here. */
-#preferences .oo-ui-panelLayout-framed {
- border-color: #c8ccd1;
-}
+.mw-prefs-tabs {
+ .mw-prefs-fieldset-wrapper {
+ padding-left: 0;
+ padding-right: 0;
+
+ &:first-child {
+ padding-top: 0;
+ }
-#preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
- border-width: 0;
- border-radius: 0;
- padding-left: 0;
- padding-right: 0;
- box-shadow: none;
+ &:last-child {
+ padding-bottom: 0;
+ }
+ }
}
-.mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-menu a {
- color: inherit;
- text-decoration: none;
+.mw-prefs-tabs-wrapper.oo-ui-panelLayout-framed,
+.mw-prefs-tabs > .oo-ui-menuLayout-content > .oo-ui-indexLayout-stackLayout > .oo-ui-tabPanelLayout {
+ /* Decrease contrast of `border` slightly as padding/border combination is sufficient
+ * accessibility wise and focus of content is more important here. */
+ border-color: #c8ccd1;
}
-/* Disabled JavaScript */
+/* JavaScript disabled */
.client-nojs {
- /* Adjust the borders: frame each prefsection instead of the
- * whole tabLayout wrapper */
- #preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:first-child {
- border-color: #c8ccd1;
- border-width: 1px 0 0;
- }
-
- #preferences .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:last-child {
- padding-bottom: 0;
- margin-bottom: 0;
- }
-
- /* Fake Tabs to address reflow */
- .mw-prefs-faketabs {
+ // Disable .oo-ui-panelLayout-framed on outer wrapper
+ .mw-prefs-tabs-wrapper {
border-width: 0;
border-radius: 0;
- .box-shadow( none );
+ }
- > .oo-ui-menuLayout > .oo-ui-menuLayout-content > .oo-ui-stackLayout {
- margin-bottom: 1em;
+ .mw-prefs-tabs {
+ // Hide the tab menu when JS is disabled as we can't use this feature
+ > .oo-ui-menuLayout-menu {
+ display: none;
}
- /* Hide the tab menu when JS is disabled as we can't use this feature */
- > .oo-ui-menuLayout > .oo-ui-menuLayout-menu {
- display: none;
+ .mw-prefs-section-fieldset {
+ // <legend> is hard to style, so apply border to top of group
+ > .oo-ui-fieldsetLayout-group {
+ padding-top: 1.5em;
+ border-top: 1px solid #c8ccd1;
+ }
+
+ // Remove spacing between legend and underline
+ &.oo-ui-labelElement > .oo-ui-fieldsetLayout-header > .oo-ui-labelElement-label {
+ margin-bottom: 0;
+ }
+ }
+
+ // Spacing between sections
+ > .oo-ui-menuLayout-content > .oo-ui-indexLayout-stackLayout > .oo-ui-tabPanelLayout {
+ margin-bottom: 1em;
}
}
}
-/* Enabled JavaScript
- * Hide top level legends when JS is enabled, as they will not be visible
- * when the real tabLayout is built */
-.client-js #preferences {
+/* JavaScript enabled */
+.client-js .mw-prefs-tabs {
.oo-ui-tabPanelLayout {
- padding-top: 0.5em;
+ // Panels don't need borders as the IndexLayout is inside a framed wrapper.
+ border: 0;
- & > fieldset > legend {
+ // Hide section legend, only used in nojs mode
+ > fieldset > legend {
display: none;
}
}
- .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
- margin-top: 2.286em; /* equals `32px` at `font-size: 14px;` */
- margin-bottom: 0;
- border-width: 0;
- border-radius: 0;
- padding: 0;
- box-shadow: none;
-
- &:first-child {
- margin-top: 0.85714286em;
- }
-
- .oo-ui-panelLayout-framed:first-child {
- margin-top: 0;
+ // Hide all but the first panel before infusion
+ &:not( .mw-prefs-tabs-infused ) {
+ .oo-ui-tabPanelLayout:not( :first-child ) {
+ display: none;
}
}
-
- > .oo-ui-panelLayout > .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header {
- margin-bottom: 1em;
- }
}
/* Make the "Basic information" section more compact */