* Added $wgParserTestMediaHandlers, where mock media handlers can be passed to
MediaHandlerFactory for parser tests.
-=== Languages updated in 1.30 ===
-
-* Support for kbp (Kabɩyɛ / Kabiyè) was added.
-* Support for skr (Saraiki, سرائیکی) was added.
-
=== External library changes in 1.30 ===
==== Upgraded external libraries ====
regularly. Below only new and removed languages are listed, as well as
changes to languages because of Phabricator reports.
-* …
+* Added: kbp (Kabɩyɛ / Kabiyè)
+* Added: skr (Saraiki, سرائیکی)
+* Added: tay (Tayal / Atayal)
==== Pig Latin added ====
* (T45547) Added Pig Latin, a made-up English variant (en-x-piglatin),
$wgOut->addHTML( Html::openElement(
'form',
[
- // Keep mw-editform-ooui class for backwards-compatibility temporarily
- 'class' => 'mw-editform mw-editform-ooui',
+ 'class' => 'mw-editform',
'id' => self::EDITFORM_ID,
'name' => self::EDITFORM_ID,
'method' => 'post',
public function checkAccountCreatePermissions( User $creator ) {
// Wiki is read-only?
if ( wfReadOnly() ) {
- return Status::newFatal( 'readonlytext', wfReadOnlyReason() );
+ return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
}
// This is awful, this permission check really shouldn't go through Title.
] );
$user->setId( 0 );
$user->loadFromId();
- return Status::newFatal( 'readonlytext', wfReadOnlyReason() );
+ return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
}
// Check the session, if we tried to create this user already there's
global $wgCascadingRestrictionLevels, $wgContLang;
if ( wfReadOnly() ) {
- return Status::newFatal( 'readonlytext', wfReadOnlyReason() );
+ return Status::newFatal( wfMessage( 'readonlytext', wfReadOnlyReason() ) );
}
$this->loadPageData( 'fromdbmaster' );
parent::execute( $subpage );
if ( $this->isStructuredFilterUiEnabled() ) {
+ $output->addModuleStyles( [ 'mediawiki.rcfilters.highlightCircles.seenunseen.styles' ] );
+
$output->addJsConfigVars( 'wgStructuredChangeFiltersLiveUpdateSupported', false );
$output->addJsConfigVars(
'wgStructuredChangeFiltersSavedQueriesPreferenceName',
'sw' => 'Kiswahili', # Swahili
'szl' => 'ślůnski', # Silesian
'ta' => 'தமிழ்', # Tamil
+ 'tay' => 'Tayal', # Atayal
'tcy' => 'ತುಳು', # Tulu
'te' => 'తెలుగు', # Telugu
'tet' => 'tetun', # Tetun
--- /dev/null
+<?php
+/** Atayal (Tayal)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ */
+
+$fallback = 'zh-hant';
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.hlist' => [
+ 'targets' => [ 'desktop', 'mobile' ],
'styles' => [
'resources/src/mediawiki/mediawiki.hlist-allskins.less',
],
],
'mediawiki.action.edit.styles' => [
'targets' => [ 'desktop', 'mobile' ],
- 'styles' => 'resources/src/mediawiki.action/mediawiki.action.edit.styles.css',
+ 'styles' => 'resources/src/mediawiki.action/mediawiki.action.edit.styles.less',
],
'mediawiki.action.edit.collapsibleFooter' => [
'scripts' => 'resources/src/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js',
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less',
],
],
+ 'mediawiki.rcfilters.highlightCircles.seenunseen.styles' => [
+ 'styles' => [
+ 'resources/src/mediawiki.rcfilters/' .
+ 'styles/mw.rcfilters.ui.ChangesListWrapperWidget.highlightCircles.seenunseen.less',
+ ],
+ ],
'mediawiki.rcfilters.filters.dm' => [
'scripts' => [
'resources/src/mediawiki.rcfilters/mw.rcfilters.js',
'styles' => 'resources/src/mediawiki.special/mediawiki.special.upload.styles.css',
],
'mediawiki.special.userlogin.common.styles' => [
- 'styles' => [
- 'resources/src/mediawiki.special/mediawiki.special.userlogin.common.css',
+ 'targets' => [ 'desktop', 'mobile' ],
+ 'skinStyles' => [
+ 'default' => 'resources/src/mediawiki.special/mediawiki.special.userlogin.common.css',
],
],
'mediawiki.special.userlogin.login.styles' => [
+++ /dev/null
-/*!
- * Styles for elements of the editing form.
- */
-
-/* General layout */
-#wpTextbox1 {
- margin: 0;
- display: block;
- /* Ensure the textarea is not higher than browser's viewport on small screens */
- max-height: 100vh;
- /* But don't let it collapse into nothingness on really tiny screens */
- min-height: 5em;
-}
-
-/*
- * Add a bit of margin space between the preview and the toolbar.
- * This replaces the ugly <p><br /></p> we used to insert into the page source
- */
-#wikiPreview.ontop {
- margin-bottom: 1em;
-}
-
-/* Adjustments to edit form elements */
-#editpage-copywarn {
- font-size: 0.9em;
-}
-
-.mw-editform-ooui #wpSummaryWidget {
- display: block;
- margin-bottom: 1em;
- max-width: none;
-}
-
-.mw-editform-ooui #editpage-copywarn {
- line-height: 1.26;
-}
-
-.mw-editform-ooui #wpSummaryLabel {
- margin: 0;
-}
-
-.mw-editform-ooui .editCheckboxes .oo-ui-fieldLayout {
- margin-right: 1em;
-}
-
-.mw-editform-ooui .editHelp {
- margin-left: 0.5em;
- vertical-align: middle;
-}
-
-.mw-editform-ooui .editHelp a {
- font-weight: bold;
-}
-
-.mw-editform-ooui .editOptions {
- border-radius: 0 0 2px 2px;
-}
--- /dev/null
+/*!
+ * Styles for elements of the editing form.
+ */
+
+/*
+ * Add a bit of margin space between the preview and the toolbar.
+ * This replaces the ugly <p><br /></p> we used to insert into the page source
+ */
+#wikiPreview.ontop {
+ margin-bottom: 1em;
+}
+
+.mw-editform {
+ /* General layout */
+ #wpTextbox1 {
+ margin: 0;
+ display: block;
+ /* Ensure the textarea is not higher than browser's viewport on small screens */
+ max-height: 100vh;
+ /* But don't let it collapse into nothingness on really tiny screens */
+ min-height: 5em;
+ }
+
+ /* Adjustments to edit form elements */
+ #editpage-copywarn {
+ font-size: 0.9em;
+ line-height: 1.26;
+ }
+
+ #wpSummaryWidget {
+ display: block;
+ margin-bottom: 1em;
+ max-width: none;
+ }
+
+ #wpSummaryLabel {
+ margin: 0;
+ }
+
+ .editCheckboxes .oo-ui-fieldLayout {
+ margin-right: 1em;
+ }
+
+ .editHelp {
+ margin-left: 0.5em;
+ vertical-align: middle;
+
+ a {
+ font-weight: bold;
+ }
+ }
+
+ .editOptions {
+ border-radius: 0 0 2px 2px;
+ }
+}
OO.EmitterList.call( this );
this.default = config.default;
+ this.baseState = {};
// Events
this.aggregate( { update: 'itemUpdate' } );
* @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
*/
mw.rcfilters.dm.SavedQueriesModel.prototype.findMatchingQuery = function ( fullQueryComparison ) {
+ var model = this;
+
+ fullQueryComparison = this.getDifferenceFromBase( fullQueryComparison );
+
return this.getItems().filter( function ( item ) {
+ var comparedData = model.getDifferenceFromBase( item.getData() );
return OO.compare(
- item.getData(),
+ comparedData,
fullQueryComparison
);
} )[ 0 ];
};
+ /**
+ * Get a minimal representation of the state for comparison
+ *
+ * @param {Object} state Given state
+ * @return {Object} Minimal state
+ */
+ mw.rcfilters.dm.SavedQueriesModel.prototype.getDifferenceFromBase = function ( state ) {
+ var result = { filters: {}, highlights: {}, invert: state.invert },
+ baseState = this.baseState;
+
+ // XOR results
+ $.each( state.filters, function ( name, value ) {
+ if ( baseState.filters !== undefined && baseState.filters[ name ] !== value ) {
+ result.filters[ name ] = value;
+ }
+ } );
+
+ $.each( state.highlights, function ( name, value ) {
+ if ( baseState.highlights !== undefined && baseState.highlights[ name ] !== value && name !== 'highlight' ) {
+ result.highlights[ name ] = value;
+ }
+ } );
+
+ return result;
+ };
/**
* Get query by its identifier
*
}
// This is a general mixin for a color circle
-.mw-rcfilters-mixin-circle( @color: #fff, @diameter: 2em, @padding: 0.5em, @border: false ) {
- background-color: @color;
+.mw-rcfilters-mixin-circle( @color: #fff, @diameter: 2em, @padding: 0.5em, @border: false, @borderColor: #54595d, @emptyBackground: false ) {
.box-sizing( border-box );
min-width: @diameter;
width: @diameter;
margin: @padding;
border-radius: 50%;
+ & when ( @emptyBackground = false ) {
+ background-color: @color;
+ }
+ & when ( @emptyBackground = true ) {
+ background-color: @highlight-none;
+ }
+
& when ( @border = true ) {
- border: 1px solid #54595d;
+ border: 1px solid @borderColor;
}
}
--- /dev/null
+@import 'mw.rcfilters.mixins';
+
+.mw-rcfilters-ui-changesListWrapperWidget {
+ ul {
+ list-style: none;
+
+ li {
+ list-style: none;
+ }
+ }
+
+ // Make more specific for the overrides
+ div&-highlights {
+ display: inline-block;
+
+ &-color {
+ &-none {
+ display: inline-block;
+ li.mw-changeslist-watchedseen & {
+ .mw-rcfilters-ui-changesListWrapperWidget.mw-rcfilters-ui-changesListWrapperWidget-highlighted & {
+ .mw-rcfilters-mixin-circle( @highlight-none, @result-circle-diameter, 0, true, @highlight-grey, true );
+ }
+
+ .mw-rcfilters-ui-changesListWrapperWidget:not(.mw-rcfilters-ui-changesListWrapperWidget-highlighted) & {
+ .mw-rcfilters-mixin-circle( @highlight-none, @result-circle-diameter, 0, true, @highlight-bluedot, true );
+ }
+ }
+
+ li.mw-changeslist-watchedunseen & {
+ .mw-rcfilters-ui-changesListWrapperWidget.mw-rcfilters-ui-changesListWrapperWidget-highlighted & {
+ .mw-rcfilters-mixin-circle( @highlight-grey, @result-circle-diameter, 0, true, @highlight-grey );
+ }
+
+ .mw-rcfilters-ui-changesListWrapperWidget:not(.mw-rcfilters-ui-changesListWrapperWidget-highlighted) & {
+ .mw-rcfilters-mixin-circle( @highlight-bluedot, @result-circle-diameter, 0, true, @highlight-bluedot );
+ }
+ }
+
+ }
+
+ // Watchlist unseen highlighted fixes
+ // Seen (empty circle)
+ // There's no need to correct 'unseen' because that would be
+ // a filled colorful circle, which is the regular rendering
+ .mw-changeslist-watchedseen &-c1 {
+ .mw-rcfilters-mixin-circle( @highlight-c1, @result-circle-diameter, 0, true, @highlight-c1, true );
+ }
+
+ .mw-changeslist-watchedseen &-c2 {
+ .mw-rcfilters-mixin-circle( @highlight-c2, @result-circle-diameter, 0, true, @highlight-c2, true );
+ }
+
+ .mw-changeslist-watchedseen &-c3 {
+ .mw-rcfilters-mixin-circle( @highlight-c3, @result-circle-diameter, 0, true, @highlight-c3, true );
+ }
+
+ .mw-changeslist-watchedseen &-c4 {
+ .mw-rcfilters-mixin-circle( @highlight-c4, @result-circle-diameter, 0, true, @highlight-c4, true );
+ }
+
+ .mw-changeslist-watchedseen &-c5 {
+ .mw-rcfilters-mixin-circle( @highlight-c5, @result-circle-diameter, 0, true, @highlight-c5, true );
+ }
+ }
+ }
+}
display: inline-block;
}
- div {
+ // This needs to be very specific, since these are
+ // position rules that should apply to all overrides
+ .mw-rcfilters-ui-changesListWrapperWidget .mw-rcfilters-ui-changesListWrapperWidget-highlights > div&-circle {
.box-sizing( border-box );
margin-right: @result-circle-margin;
vertical-align: middle;
@highlight-c3: #fc3;
@highlight-c4: #ff6d22;
@highlight-c5: #d33;
+@highlight-bluedot: #1d4aad; // Simulates the 'known' browser <li> blue dot
+@highlight-grey: #54595d; // The color of full dots on Watchlist when highlight is enabled
// Muted state
@muted-opacity: 0.5;
.addClass( highlightClass )
.append(
$( '<div>' )
+ .addClass( 'mw-rcfilters-ui-changesListWrapperWidget-highlights-circle' )
.addClass( 'mw-rcfilters-ui-changesListWrapperWidget-highlights-color-none' )
.prop( 'data-color', 'none' )
);
$highlights.append(
$( '<div>' )
.addClass( 'mw-rcfilters-ui-changesListWrapperWidget-highlights-color-' + color )
+ .addClass( 'mw-rcfilters-ui-changesListWrapperWidget-highlights-circle' )
.prop( 'data-color', color )
);
} );
}
.iw-result__footer a {
vertical-align: middle;
- color: @colorGray7;
font-style: italic;
}
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$this->assertEquals(
- \Status::newFatal( 'readonlytext', 'Because' ),
+ \Status::newFatal( wfMessage( 'readonlytext', 'Because' ) ),
$this->manager->checkAccountCreatePermissions( new \User )
);
$readOnlyMode->setReason( false );
$this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_SESSION, true );
$this->unhook( 'LocalUserCreated' );
- $this->assertEquals( \Status::newFatal( 'readonlytext', 'Because' ), $ret );
+ $this->assertEquals( \Status::newFatal( wfMessage( 'readonlytext', 'Because' ) ), $ret );
$this->assertEquals( 0, $user->getId() );
$this->assertNotEquals( $username, $user->getName() );
$this->assertEquals( 0, $session->getUser()->getId() );