source 'https://rubygems.org'
-gem 'mediawiki_selenium', '~> 1.6.5'
+gem 'mediawiki_selenium', '~> 1.7'
+gem 'rake', '~> 11.1', '>= 11.1.1'
gem 'rubocop', '~> 0.32.1', require: false
faker (>= 1.1.2)
yml_reader (>= 0.6)
diff-lcs (1.2.5)
- domain_name (0.5.20160128)
+ domain_name (0.5.20160310)
unf (>= 0.0.5, < 1.0.0)
- faker (1.6.1)
+ faker (1.6.3)
i18n (~> 0.5)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
ffi (1.9.10)
gherkin (2.12.2)
multi_json (~> 1.3)
- headless (2.2.0)
+ headless (2.2.3)
http-cookie (1.0.2)
domain_name (~> 0.5)
i18n (0.7.0)
mediawiki_api (0.5.0)
faraday (~> 0.9, >= 0.9.0)
faraday-cookie_jar (~> 0.0, >= 0.0.6)
- mediawiki_selenium (1.6.5)
+ mediawiki_selenium (1.7.0)
cucumber (~> 1.3, >= 1.3.20)
headless (~> 2.0, >= 2.1.0)
json (~> 1.8, >= 1.8.1)
rspec-expectations (~> 2.14, >= 2.14.4)
syntax (~> 1.2, >= 1.2.0)
thor (~> 0.19, >= 0.19.1)
- mime-types (2.99)
- multi_json (1.11.2)
+ mime-types (2.99.1)
+ multi_json (1.11.3)
multi_test (0.1.2)
multipart-post (2.0.0)
netrc (0.11.0)
ast (>= 1.1, < 3.0)
powerpack (0.1.1)
rainbow (2.0.0)
+ rake (11.1.1)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.5)
- rubyzip (1.1.7)
- selenium-webdriver (2.50.0)
+ rubyzip (1.2.0)
+ selenium-webdriver (2.53.0)
childprocess (~> 0.5)
- multi_json (~> 1.0)
rubyzip (~> 1.0)
websocket (~> 1.0)
syntax (1.2.0)
thor (0.19.1)
unf (0.1.4)
unf_ext
- unf_ext (0.0.7.1)
+ unf_ext (0.0.7.2)
watir-webdriver (0.9.1)
selenium-webdriver (>= 2.46.2)
- websocket (1.2.2)
+ websocket (1.2.3)
yml_reader (0.7)
PLATFORMS
ruby
DEPENDENCIES
- mediawiki_selenium (~> 1.6.5)
+ mediawiki_selenium (~> 1.7)
+ rake (~> 11.1, >= 11.1.1)
rubocop (~> 0.32.1)
+
+BUNDLED WITH
+ 1.10.6
task.options = ['-c', '.rubocop.yml']
end
+require 'mediawiki_selenium/rake_task'
+MediawikiSelenium::RakeTask.new(site_tag: false)
+
task default: [:test]
desc 'Run all build/tests commands (CI entry point)'
*
* The format is an associative array where the key is a cache identifier, and
* the value is an associative array of parameters. The "cacheId" parameter is
- * a cache identifier from $wgObjectCaches. The "relayerConfig" parameter is an
- * array used to construct an EventRelayer object. The "pool" parameter is a
- * string that is used as a PubSub channel prefix. The "loggroup" parameter
- * controls where log events are sent.
+ * a cache identifier from $wgObjectCaches. The "channels" parameter is a map of
+ * actions ('purge') to PubSub channels defined in $wgEventRelayerConfig.
+ * The "loggroup" parameter controls where log events are sent.
*
* @since 1.26
*/
$wgWANObjectCaches = [
CACHE_NONE => [
- 'class' => 'WANObjectCache',
- 'cacheId' => CACHE_NONE,
- 'pool' => 'mediawiki-main-none',
- 'relayerConfig' => [ 'class' => 'EventRelayerNull' ]
+ 'class' => 'WANObjectCache',
+ 'cacheId' => CACHE_NONE,
+ 'channels' => []
]
/* Example of a simple single data-center cache:
- 'memcached-php' => array(
- 'class' => 'WANObjectCache',
- 'cacheId' => 'memcached-php',
- 'pool' => 'mediawiki-main-memcached',
- 'relayerConfig' => array( 'class' => 'EventRelayerNull' )
- )
+ 'memcached-php' => [
+ 'class' => 'WANObjectCache',
+ 'cacheId' => 'memcached-php',
+ 'channels' => [ 'purge' => 'wancache-main-memcached-purge' ]
+ ]
*/
];
$wgMaxUserDBWriteDuration = false;
/**
- * Mapping of event channels to EventRelayer configuration.
+ * Mapping of event channels (or channel categories) to EventRelayer configuration.
*
* By setting up a PubSub system (like Kafka) and enabling a corresponding EventRelayer class
* that uses it, MediaWiki can broadcast events to all subscribers. Certain features like WAN
* subscribe to the channel and take actions based on the events. For example, a local daemon
* can run on each CDN cache node and perfom local purges based on the URL purge channel events.
*
- * The 'default' channel is for all channels without an explicit entry here.
+ * Some extensions may want to use "channel categories" so that different channels can also share
+ * the same custom relayer instance (e.g. when it's likely to be overriden). They can use
+ * EventRelayerGroup::getRelayer() based on the category but call notify() on various different
+ * actual channels. One reason for this would be that some system have very different performance
+ * vs durability needs, so one system (e.g. Kafka) may not be suitable for all uses.
+ *
+ * The 'default' key is for all channels (or channel categories) without an explicit entry here.
*
* @since 1.27
*/
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* Factory class for spawning EventRelayer objects using configuration
*
/** @var EventRelayer[] */
protected $relayers = [];
- /** @var EventRelayerGroup */
- protected static $instance = null;
-
/**
- * @param Config $config
+ * @param array[] $config Channel configuration
*/
- protected function __construct( Config $config ) {
- $this->configByChannel = $config->get( 'EventRelayerConfig' );
+ public function __construct( array $config ) {
+ $this->configByChannel = $config;
}
/**
+ * @deprecated since 1.27 Use MediaWikiServices::getInstance()->getEventRelayerGroup()
* @return EventRelayerGroup
*/
public static function singleton() {
- if ( !self::$instance ) {
- self::$instance = new self( RequestContext::getMain()->getConfig() );
- }
-
- return self::$instance;
+ return MediaWikiServices::getInstance()->getEventRelayerGroup();
}
/**
return $this->getService( 'StatsdDataFactory' );
}
+ /**
+ * @return EventRelayerGroup
+ */
+ public function getEventRelayerGroup() {
+ return $this->getService( 'EventRelayerGroup' );
+ }
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service getter here, don't forget to add a test
// case for it in MediaWikiServicesTest::provideGetters() and in
);
},
+ 'EventRelayerGroup' => function( MediaWikiServices $services ) {
+ return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) );
+ },
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service here, don't forget to add a getter function
// in the MediaWikiServices class. The convenience getter should just call
// Sites using multiple datacenters can configure a relayer.
$wgMainWANCache = 'mediawiki-main-default';
$wgWANObjectCaches[$wgMainWANCache] = [
- 'class' => 'WANObjectCache',
- 'cacheId' => $wgMainCacheType,
- 'pool' => 'mediawiki-main-default',
- 'relayerConfig' => [ 'class' => 'EventRelayerNull' ]
+ 'class' => 'WANObjectCache',
+ 'cacheId' => $wgMainCacheType,
+ 'channels' => [ 'purge' => 'wancache-main-default-purge' ]
];
}
*/
use Wikimedia\Assert\Assert;
+use MediaWiki\MediaWikiServices;
/**
* Handles purging appropriate CDN URLs given a title (or titles)
wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) );
// Reliably broadcast the purge to all edge nodes
- $relayer = EventRelayerGroup::singleton()->getRelayer( 'cdn-url-purges' );
+ $relayer = MediaWikiServices::getInstance()->getEventRelayerGroup()
+ ->getRelayer( 'cdn-url-purges' );
$relayer->notify(
'cdn-url-purges',
[
return $this->doNotify( $channel, $events );
}
- /**
- * Set logger instance.
- * @param LoggerInterface $logger
- */
public function setLogger( LoggerInterface $logger ) {
$this->logger = $logger;
}
protected $cache;
/** @var HashBagOStuff Script instance PHP cache */
protected $procCache;
- /** @var string Cache pool name */
- protected $pool;
+ /** @var string Purge channel name */
+ protected $purgeChannel;
/** @var EventRelayer Bus that handles purge broadcasts */
- protected $relayer;
+ protected $purgeRelayer;
/** @var LoggerInterface */
protected $logger;
const MAX_PC_KEYS = 1000; // max keys to keep in process cache
+ const DEFAULT_PURGE_CHANNEL = 'wancache-purge';
+
/**
* @param array $params
- * - cache : BagOStuff object
- * - pool : pool name
- * - relayer : EventRelayer object
- * - logger : LoggerInterface object
+ * - cache : BagOStuff object for a persistent cache
+ * - channels : Map of (action => channel string). Actions include "purge".
+ * - relayers : Map of (action => EventRelayer object). Actions include "purge".
+ * - logger : LoggerInterface object
*/
public function __construct( array $params ) {
$this->cache = $params['cache'];
- $this->pool = $params['pool'];
- $this->relayer = $params['relayer'];
$this->procCache = new HashBagOStuff( [ 'maxKeys' => self::MAX_PC_KEYS ] );
+ $this->purgeChannel = isset( $params['channels']['purge'] )
+ ? $params['channels']['purge']
+ : self::DEFAULT_PURGE_CHANNEL;
+ $this->purgeRelayer = isset( $params['relayers']['purge'] )
+ ? $params['relayers']['purge']
+ : new EventRelayerNull( [] );
$this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() );
}
- /**
- * Set logger instance.
- * @param LoggerInterface $logger
- */
public function setLogger( LoggerInterface $logger ) {
$this->logger = $logger;
}
*
* return CatConfig::newFromRow( $dbr->selectRow( ... ) );
* },
- * array(
+ * [
* // Calling touchCheckKey() on this key invalidates the cache
- * 'checkKeys' => array( $cache->makeKey( 'site-cat-config' ) ),
+ * 'checkKeys' => [ $cache->makeKey( 'site-cat-config' ) ],
* // Try to only let one datacenter thread manage cache updates at a time
* 'lockTSE' => 30
- * )
+ * ]
* );
* @endcode
*
*
* return CatState::newFromResults( $dbr->select( ... ) );
* },
- * array(
+ * [
* // The "check" keys that represent things the value depends on;
* // Calling touchCheckKey() on any of them invalidates the cache
- * 'checkKeys' => array(
+ * 'checkKeys' => [
* $cache->makeKey( 'sustenance-bowls', $cat->getRoomId() ),
* $cache->makeKey( 'people-present', $cat->getHouseId() ),
* $cache->makeKey( 'cat-laws', $cat->getCityId() ),
- * )
- * )
+ * ]
+ * ]
* );
* @endcode
*
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* // Start off with the last cached list
- * $list = $oldValue ?: array();
+ * $list = $oldValue ?: [];
* // Fetch the last 100 relevant rows in descending order;
* // only fetch rows newer than $list[0] to reduce scanning
* $rows = iterator_to_array( $dbr->select( ... ) );
* return array_slice( array_merge( $new, $list ), 0, 100 );
* },
* // Try to only let one datacenter thread manage cache updates at a time
- * array( 'lockTSE' => 30 )
+ * [ 'lockTSE' => 30 ]
* );
* @endcode
*
'sbt' => true, // substitute $UNIXTIME$ with actual microtime
] );
- $ok = $this->relayer->notify( "{$this->pool}:purge", $event );
+ $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
if ( !$ok ) {
$this->lastRelayError = self::ERR_RELAY;
}
'key' => $key,
] );
- $ok = $this->relayer->notify( "{$this->pool}:purge", $event );
+ $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
if ( !$ok ) {
$this->lastRelayError = self::ERR_RELAY;
}
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* Functions to get cache objects
}
$params = $wgWANObjectCaches[$id];
- $class = $params['relayerConfig']['class'];
- $params['relayer'] = new $class( $params['relayerConfig'] );
+ foreach ( $params['channels'] as $action => $channel ) {
+ $params['relayers'][$action] = MediaWikiServices::getInstance()->getEventRelayerGroup()
+ ->getRelayer( $channel );
+ $params['channels'][$action] = $channel;
+ }
$params['cache'] = self::newFromId( $params['cacheId'] );
if ( isset( $params['loggroup'] ) ) {
$params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
if ( $t->hasFragment() ) {
# Strip the fragment to avoid various odd effects
- $this->mTitle = clone $t;
- $this->mTitle->setFragment( '' );
+ $this->mTitle = $t->createFragmentTarget( '' );
} else {
$this->mTitle = $t;
}
throw new UserBlockedError( $user->getBlock() );
}
+ // Global blocks
+ if ( $user->isBlockedGlobally() ) {
+ throw new UserBlockedError( $user->getGlobalBlock() );
+ }
+
# Check whether we actually want to allow changing stuff
$this->checkReadOnly();
protected $mImplicitGroups;
/** @var array */
protected $mFormerGroups;
- /** @var bool */
- protected $mBlockedGlobally;
+ /** @var Block */
+ protected $mGlobalBlock;
/** @var bool */
protected $mLocked;
/** @var bool */
* @return bool True if blocked, false otherwise
*/
public function isBlockedGlobally( $ip = '' ) {
- if ( $this->mBlockedGlobally !== null ) {
- return $this->mBlockedGlobally;
+ return $this->getGlobalBlock( $ip ) instanceof Block;
+ }
+
+ /**
+ * Check if user is blocked on all wikis.
+ * Do not use for actual edit permission checks!
+ * This is intended for quick UI checks.
+ *
+ * @param string $ip IP address, uses current client if none given
+ * @return Block|null Block object if blocked, null otherwise
+ * @throws FatalError
+ * @throws MWException
+ */
+ public function getGlobalBlock( $ip = '' ) {
+ if ( $this->mGlobalBlock !== null ) {
+ return $this->mGlobalBlock ?: null;
}
// User is already an IP?
if ( IP::isIPAddress( $this->getName() ) ) {
$ip = $this->getRequest()->getIP();
}
$blocked = false;
- Hooks::run( 'UserIsBlockedGlobally', [ &$this, $ip, &$blocked ] );
- $this->mBlockedGlobally = (bool)$blocked;
- return $this->mBlockedGlobally;
+ $block = null;
+ Hooks::run( 'UserIsBlockedGlobally', [ &$this, $ip, &$blocked, &$block ] );
+
+ if ( $blocked && $block === null ) {
+ // back-compat: UserIsBlockedGlobally didn't have $block param first
+ $block = new Block;
+ $block->setTarget( $ip );
+ }
+
+ $this->mGlobalBlock = $blocked ? $block : false;
+ return $this->mGlobalBlock ?: null;
}
/**
"apisandbox-submit-invalid-fields-message": "Калі ласка, выпраўце пазначаныя палі і паспрабуйце яшчэ раз.",
"apisandbox-results": "Вынікі",
"apisandbox-sending-request": "Адпраўка API-запыту…",
+ "apisandbox-loading-results": "Атрымліваем API-вынікі…",
"apisandbox-request-url-label": "URL-адрас запыту:",
"apisandbox-request-time": "Час запыту: {{PLURAL:$1|$1 мс}}",
"booksources": "Крыніцы кніг",
"action-createpage": "krei paĝojn",
"action-createtalk": "krei diskuto-paĝojn",
"action-createaccount": "krei ĉi tiun uzanto-konton",
+ "action-autocreateaccount": "Aŭtomate krei tiun eksteruzantan konton",
"action-history": "vidi historion de tiu ĉi paĝo",
"action-minoredit": "marki ĉi tiun redakton eta",
"action-move": "movi ĉi tiun paĝon",
"rcshowhidemine": "$1 miajn redaktojn",
"rcshowhidemine-show": "Montri",
"rcshowhidemine-hide": "Kaŝi",
+ "rcshowhidecategorization": "$1 paĝokategoriadon",
"rcshowhidecategorization-show": "Montri",
"rcshowhidecategorization-hide": "Kaŝi",
"rclinks": "Montri $1 lastajn ŝanĝojn dum la $2 lastaj tagoj.<br />$3",
"recentchangeslinked-summary": "Jen listo de ŝanĝoj faritaj lastatempe al paĝoj ligitaj el specifa paĝo (aŭ al membroj de specifa kategorio).\nPaĝoj en [[Special:Watchlist|via atentaro]] estas '''grasaj'''.",
"recentchangeslinked-page": "Nomo de paĝo:",
"recentchangeslinked-to": "Montru ŝanĝojn al paĝoj ligitaj al la specifa paĝo anstataŭe.",
+ "recentchanges-page-added-to-category": "[[:$1]] kategorialdonita",
+ "recentchanges-page-added-to-category-bundled": "[[:$1]] kategorialdonita, [[Special:WhatLinksHere/$1|tiu paĝo estas inkluzivita ene de aliaj paĝoj]]",
+ "recentchanges-page-removed-from-category": "[[:$1]] kategoriforigita",
+ "recentchanges-page-removed-from-category-bundled": "[[:$1]] kategoriforigita, [[Special:WhatLinksHere/$1|tiu paĝo estas inkluzivita ene de aliaj paĝoj]]",
+ "autochange-username": "Aŭtomata ŝanĝo de Mediavikio",
"upload": "Alŝuti dosieron",
"uploadbtn": "Alŝuti dosieron",
"reuploaddesc": "Reveni al la alŝuta formularo.",
"uploaddisabledtext": "Alŝutado de dosieroj estas malebligita.",
"php-uploaddisabledtext": "Dosiera alŝutado estas malŝalta en PHP. Bonvolu kontroli la preferon file_uploads.",
"uploadscripted": "HTML-aĵo aŭ skriptokodaĵo troviĝas en tiu ĉi tiu dosiero, kiun TTT-foliumilo eble interpretus erare.",
+ "upload-scripted-pi-callback": "Malalŝuteblas dosieron, kiu enhavas instrukcion de XML-stilfolia traktado",
+ "uploaded-script-svg": "Trovis skriptelbero \"$1\" en la alŝutita SVGa dosiero.",
+ "uploaded-hostile-svg": "Trovis malsekura CSS en la stilero de alŝutita SVGa dosiero.",
+ "uploaded-event-handler-on-svg": "Ensigni eventotraktilajn atributojn <code>$1=\"$2\"</code> estas malpermisita en SVGaj dosieroj.",
+ "uploaded-href-attribute-svg": "Atributoj je \"href\" en SVGaj dosieroj nur povas ligi al \"http://\" aŭ \"https://\" celoj, trovis <code><$1 $2=\"$3\"></code>.",
+ "uploaded-href-unsafe-target-svg": "Trovis je \"href\" ligita al malsekuraj datenoj: URIa celo <code><$1 $2=\"$3\"></code> en la alŝuta SVGa dosiero.",
+ "uploaded-animate-svg": "Trovis markon je \"animate\", kiu povus ŝanĝi la atributon je \"href\", per uzi la atributon je \"from\" <code><$1 $2=\"$3\"></code> en la alŝuta SVGa dosiero.",
+ "uploaded-setting-event-handler-svg": "Ensigni eventotraktilajn atributojn estas blokita, trovis <code><$1 $2=\"$3\"></code> en la alŝuta SVGa dosiero.",
+ "uploaded-setting-href-svg": "Uzi la markon je \"set\" por aldoni atributon je \"href\" al ujero estas blokita.",
"uploadscriptednamespace": "Ĉi tiu SVG-dosiero enhavas nevalidan nomspacon \"$1\"",
"uploadinvalidxml": "Ne eblas interpreti la XML-sintakson en la alŝutita dosiero",
"uploadvirus": "Viruso troviĝas en la dosiero! Detaloj: $1",
"anonusers": "{{PLURAL:$2|l'utilisateur anonyme|les utilisateurs anonymes}} $1 de {{SITENAME}}",
"creditspage": "Crédits de la page",
"nocredits": "Il n'y a pas d'informations d'attribution disponibles pour cette page.",
- "spamprotectiontitle": "Filtre de protection anti-spam",
+ "spamprotectiontitle": "Filtre de protection anti-pollupostage",
"spamprotectiontext": "La page que vous avez voulu sauvegarder a été bloquée par le filtre anti-spam. Ceci est probablement dû à l'introduction d'un lien vers un site externe apparaissant sur la liste noire.",
- "spamprotectionmatch": "Le texte suivant a déclenché notre filtre de protection anti-spam : $1",
+ "spamprotectionmatch": "Le texte suivant a déclenché notre filtre de protection anti-pollupostage : $1",
"spambot_username": "Nettoyage de pourriels par MediaWiki",
"spam_reverting": "Rétablissement de la dernière version ne contenant pas de lien vers $1",
"spam_blanking": "Toutes les versions contenant des liens vers $1 sont blanchies",
"undeletedrevisions-files": "{{PLURAL:$1|גרסה אחת|$1 גרסאות}} ו{{PLURAL:$2|קובץ אחד|־$2 קבצים}} שוחזרו",
"undeletedfiles": "{{PLURAL:$1|שוחזר קובץ אחד|שוחזרו $1 קבצים}}",
"cannotundelete": "השחזור נכשל:\n$1",
- "undeletedpage": "'''הדף $1 שוחזר בהצלחה.'''\n\nראו את [[Special:Log/delete|יומן המחיקות]] לרשימה של מחיקות ושחזורים אחרונים.",
+ "undeletedpage": "<strong>הדף $1 שוחזר</strong>\n\nראו את [[Special:Log/delete|יומן המחיקות]] לרשימה של המחיקות והשחזורים שבוצעו לאחרונה.",
"undelete-header": "ראו את [[Special:Log/delete|יומן המחיקות]] לדפים שנמחקו לאחרונה.",
"undelete-search-title": "חיפוש דפים שנמחקו",
"undelete-search-box": "חיפוש דפים שנמחקו",
"watchlistedit-raw-done": "Jūsų stebimųjų sąrašas buvo atnaujintas.",
"watchlistedit-raw-added": "$1 {{PLURAL:$1|puslapis buvo pridėtas|puslapiai buvo pridėti|puslapių buvo pridėta}}:",
"watchlistedit-raw-removed": "$1 {{PLURAL:$1|puslapis buvo pašalintas|puslapiai buvo pašalinti|puslapių buvo pašalinta}}:",
- "watchlistedit-clear-title": "Išvalytas stebimųjų sąrašas",
+ "watchlistedit-clear-title": "Išvalyti stebimųjų sąrašą",
"watchlistedit-clear-legend": "Išvalyti stebimųjų sąrašą",
"watchlistedit-clear-explain": "Visi pavadinimai bus pašalinti iš jūsų stebimųjų sąrašo",
"watchlistedit-clear-titles": "Pavadinimai:",
"badtitle": "M̄-chiâⁿ piau-tê",
"badtitletext": "Iau-kiû ê piau-tê sī bô-hāu ê, khang ê, a̍h-sī liân-kiat chhò-gō· ê inter-language/inter-wiki piau-tê.",
"title-invalid-empty": "Só͘ iau-kiû ê ia̍h-bīn tê-bo̍k sī khang--ê he̍k-chiá kaⁿ-taⁿ hâm liáu miâ-khong-kan.",
- "title-invalid-utf8": "Só͘ iau-kiû ê ia̍h-bīn tê-bo̍k hâm liáu bô chiàⁿ-khak ê 1 lia̍t UTF-8 jī.",
+ "title-invalid-utf8": "Só͘ iau-kiû ê ia̍h-bīn tê-bo̍k hâm liáu bô chiàⁿ-khak ê 1 lia̍t UTF-8 ê jī.",
"title-invalid-interwiki": "Só͘ iau-kiû ê ia̍h-bīn tê-bo̍k pau-hâm liáu 1-ê bē-tàng ēng ùi tê-bo̍k ê interwiki liân-kiat.",
"perfcached": "Ē-kha ê chu-liāu tùi lâi--ê, só·-í bô it-tēng sī siōng sin ê. Tī khoài-chhûn-khu siōng chē ē-tàng khǹg{{PLURAL:$1| pit|$1 pit}} chu-liāu.",
"perfcachedts": "Ē-kha ê chu-liāu tùi lâi--ê, tī $1 keng-sin--koè. Tī khoài-chûn-khu siōng chē ē-tàng khǹg {{PLURAL:$4|pit|$4 pit}} chu-liāu.",
"feedback-bugnew": "Jag kontrollerade. Rapportera ett nytt fel",
"feedback-bugornote": "Om du har möjlighet att ge en detaljerad teknisk beskrivning av felet kan du lämna en [$1 buggrapport]. \nAnvänd annars formuläret nedan. Din kommentar kommer att läggas till på sidan \"[$3 $2]\", tillsammans med ditt användarnamn.",
"feedback-cancel": "Avbryt",
- "feedback-close": "Färdig",
+ "feedback-close": "Fixat",
"feedback-external-bug-report-button": "Registrera en teknisk uppgift",
"feedback-dialog-title": "Skicka återkoppling",
"feedback-dialog-intro": "Du kan använda det enkla formuläret nedan för att skicka in din återkoppling. Din kommentar kommer att läggas till på sidan \"$1\" tillsammans med ditt användarnamn.",
"log-action-filter-managetags-delete": "Märke raderad",
"log-action-filter-managetags-activate": "Märke aktiverad",
"log-action-filter-managetags-deactivate": "Märke inaktiverad",
- "log-action-filter-move-move": "Flyttning utan att skriva över omdirigeringar",
- "log-action-filter-move-move_redir": "Flyttning med att skriva över omdirigeringar",
+ "log-action-filter-move-move": "Flyttning utan överskrivning av omdirigeringar",
+ "log-action-filter-move-move_redir": "Flyttning med överskrivning av omdirigeringar",
"log-action-filter-newusers-create": "Skapade av anonyma användare",
"log-action-filter-newusers-create2": "Skapade av registrerade användare",
"log-action-filter-newusers-autocreate": "Skapades automatiskt",
"prefs-help-signature": "Tartışma sayfalarındaki yorumlar \"<nowiki>~~~~</nowiki>\" ile imzalanmalıdır, bu imzanıza ve zaman damgasına dönüştürülür.",
"badsig": "Geçersiz ham imza; HTML etiketlerini kontrol edin.",
"badsiglength": "İmzanız çok uzun.\n$1 {{PLURAL:$1|karakterin|karakterin}} altında olmalı.",
- "yourgender": "Cinsel kimlik?",
+ "yourgender": "Cinsiyet?",
"gender-unknown": "Açıklamak istemiyorum",
"gender-male": "Erkek",
"gender-female": "Kadın",
"rcshowhidemine": "Benim değişikliklerimi $1",
"rcshowhidemine-show": "göster",
"rcshowhidemine-hide": "gizle",
- "rcshowhidecategorization": "$1 sayfa kategorizasyonu",
+ "rcshowhidecategorization": "sayfa kategorizasyonunu $1",
"rcshowhidecategorization-show": "Göster",
"rcshowhidecategorization-hide": "Gizle",
"rclinks": "Son $2 günde yapılan son $1 değişikliği göster;<br /> $3",
/* Comment portions of RC entries */
span.comment {
font-style: italic;
+ unicode-bidi: -moz-isolate;
+ unicode-bidi: -webkit-isolate;
+ unicode-bidi: isolate;
}
/**
} else if ( page.invalidreason ) {
resultOut = mw.html.escape( page.invalidreason );
}
- $spinnerDestCheck.remove();
uploadWarning.processResult( resultOut, uploadWarning.nameToCheck );
+ } ).always( function () {
+ $spinnerDestCheck.remove();
} );
},
prop: 'text',
pst: true
} ).done( function ( result ) {
- $spinnerLicense.remove();
uploadLicense.processResult( result, license );
+ } ).always( function () {
+ $spinnerLicense.remove();
} );
},
url = ( ajaxOptions && ajaxOptions.url ) || this.defaults.ajax.url;
origin = ( parameters && parameters.origin ) || this.defaults.parameters.origin;
url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) +
- 'origin=' + encodeURIComponent( origin );
+ // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
+ // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
+ 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' );
newAjaxOptions = $.extend( {}, ajaxOptions, { url: url } );
} else {
newAjaxOptions = ajaxOptions;
// Prevent jQuery from overriding the Content-Type header
ajaxOptions.contentType = false;
} else {
- // Some deployed MediaWiki >= 1.17 forbid periods in URLs, due to an IE XSS bug
- // So let's escape them here. See bug #28235
// This works because jQuery accepts data as a query string or as an Object
- ajaxOptions.data = $.param( parameters ).replace( /\./g, '%2E' );
-
+ ajaxOptions.data = $.param( parameters );
// If we extracted a token parameter, add it back in.
if ( token ) {
ajaxOptions.data += '&token=' + encodeURIComponent( token );
}
+ // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
+ // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
+ ajaxOptions.data = ajaxOptions.data.replace( /\./g, '%2E' );
+
if ( ajaxOptions.contentType === 'multipart/form-data' ) {
// We were asked to emulate but can't, so drop the Content-Type header, otherwise
// it'll be wrong and the server will fail to decode the POST body
--- /dev/null
+BROWSER:
+ - firefox
+
+MEDIAWIKI_ENVIRONMENT:
+ - beta
+
+PLATFORM:
+ - Linux
'SiteStore' => [ 'getSiteStore', SiteStore::class ],
'SiteLookup' => [ 'getSiteLookup', SiteLookup::class ],
'StatsdDataFactory' => [ 'getStatsdDataFactory', StatsdDataFactory::class ],
+ 'EventRelayerGroup' => [ 'getEventRelayerGroup', EventRelayerGroup::class ],
];
}
'SiteStore' => [ 'SiteStore', SiteStore::class ],
'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory::class ],
+ 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
];
}