aboutsummaryrefslogtreecommitdiffstats
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/.htaccess1
-rw-r--r--includes/DomainEvent/DomainEvent.php1
-rw-r--r--includes/DomainEvent/DomainEventDispatcher.php1
-rw-r--r--includes/DomainEvent/DomainEventIngress.php (renamed from includes/DomainEvent/EventIngressBase.php)13
-rw-r--r--includes/DomainEvent/DomainEventSource.php1
-rw-r--r--includes/DomainEvent/DomainEventSubscriber.php3
-rw-r--r--includes/DomainEvent/InitializableDomainEventSubscriber.php1
-rw-r--r--includes/Output/OutputPage.php1
-rw-r--r--includes/ResourceLoader/ResourceLoaderEventIngress.php4
-rw-r--r--includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php12
-rw-r--r--includes/Rest/i18n/ar.json1
-rw-r--r--includes/Rest/i18n/gl.json8
-rw-r--r--includes/ServiceWiring.php5
-rw-r--r--includes/Storage/DerivedPageDataUpdater.php44
-rw-r--r--includes/Storage/PageUpdaterFactory.php3
-rw-r--r--includes/api/ApiMessageTrait.php1
-rw-r--r--includes/api/i18n/ja.json11
-rw-r--r--includes/api/i18n/pt-br.json77
-rw-r--r--includes/block/BlockUser.php5
-rw-r--r--includes/block/DatabaseBlockStore.php13
-rw-r--r--includes/composer/ComposerVendorHtaccessCreator.php4
-rw-r--r--includes/deferred/LinksUpdate/CategoryLinksTable.php10
-rw-r--r--includes/diff/DifferenceEngine.php2
-rw-r--r--includes/editpage/Constraint/AuthorizationConstraint.php88
-rw-r--r--includes/editpage/Constraint/ContentModelChangeConstraint.php35
-rw-r--r--includes/editpage/Constraint/EditConstraintFactory.php63
-rw-r--r--includes/editpage/Constraint/EditRightConstraint.php105
-rw-r--r--includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php (renamed from includes/editpage/Constraint/UserRateLimitConstraint.php)23
-rw-r--r--includes/editpage/Constraint/UserBlockConstraint.php79
-rw-r--r--includes/editpage/EditPage.php44
-rw-r--r--includes/filerepo/file/File.php6
-rw-r--r--includes/installer/Task/SqliteCreateDatabaseTask.php4
-rw-r--r--includes/installer/i18n/nan-hant.json5
-rw-r--r--includes/installer/i18n/nan-latn-pehoeji.json10
-rw-r--r--includes/installer/i18n/zh-hant.json6
-rw-r--r--includes/language/LanguageEventIngress.php4
-rw-r--r--includes/libs/filebackend/FSFileBackend.php3
-rw-r--r--includes/libs/objectcache/BagOStuff.php11
-rw-r--r--includes/libs/objectcache/WANObjectCache.php14
-rw-r--r--includes/media/MediaHandler.php10
-rw-r--r--includes/page/Article.php6
-rw-r--r--includes/page/Event/PageDeletedListener.php1
-rw-r--r--includes/page/Event/PageMovedListener.php1
-rw-r--r--includes/page/Event/PageRevisionUpdatedEvent.php4
-rw-r--r--includes/page/Event/PageRevisionUpdatedListener.php1
-rw-r--r--includes/page/Event/PageStateListener.php1
-rw-r--r--includes/pager/ContributionsPager.php12
-rw-r--r--includes/parser/ParserOptions.php3
-rw-r--r--includes/parser/Parsoid/Config/SiteConfig.php8
-rw-r--r--includes/recentchanges/ChangeTrackingEventIngress.php81
-rw-r--r--includes/search/SearchEventIngress.php4
-rw-r--r--includes/specials/SpecialVersion.php40
52 files changed, 423 insertions, 471 deletions
diff --git a/includes/.htaccess b/includes/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/includes/.htaccess
+++ b/includes/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/includes/DomainEvent/DomainEvent.php b/includes/DomainEvent/DomainEvent.php
index ec8e5399b002..cb70bd604c12 100644
--- a/includes/DomainEvent/DomainEvent.php
+++ b/includes/DomainEvent/DomainEvent.php
@@ -26,7 +26,6 @@ use Wikimedia\Timestamp\ConvertibleTimestamp;
* @note Subclasses must call declareEventType() in their constructor!
*
* @since 1.44
- * @unstable until 1.45, should become stable to extend
*/
abstract class DomainEvent {
diff --git a/includes/DomainEvent/DomainEventDispatcher.php b/includes/DomainEvent/DomainEventDispatcher.php
index 9bae3d17f807..7a8a77b8fd12 100644
--- a/includes/DomainEvent/DomainEventDispatcher.php
+++ b/includes/DomainEvent/DomainEventDispatcher.php
@@ -7,7 +7,6 @@ use Wikimedia\Rdbms\IConnectionProvider;
* Service for sending domain events to registered listeners.
*
* @since 1.44
- * @unstable until 1.45
*/
interface DomainEventDispatcher {
diff --git a/includes/DomainEvent/EventIngressBase.php b/includes/DomainEvent/DomainEventIngress.php
index fb4d65ee0002..f069914e8d99 100644
--- a/includes/DomainEvent/EventIngressBase.php
+++ b/includes/DomainEvent/DomainEventIngress.php
@@ -13,13 +13,13 @@ use LogicException;
* which event should trigger which logic in the component and for mapping from
* the model used by the emitter of the event to the component's own model.
*
- * EventIngressBase implements InitializableDomainEventSubscriber so it can be
+ * DomainEventIngress implements InitializableDomainEventSubscriber so it can be
* registered with and initialized by a DomainEventSource. Registration is
* typically done in the form of an object spec for lazy instantiation. For
* extensions' ingress objects that object spec can be provided in the
* DomainEventSubscribers section of extension.json.
*
- * After instantiating a subscriber (typically a subclass of EventIngressBase),
+ * After instantiating a subscriber (typically a subclass of DomainEventIngress),
* the event source will call initSubscriber() to initialize the subscriber and
* then registerListeners() to allow the subscriber to register listeners for
* the events it is interested in.
@@ -37,9 +37,8 @@ use LogicException;
* in extension.json.
*
* @since 1.44
- * @unstable until 1.45, should become stable to extend
*/
-abstract class EventIngressBase implements InitializableDomainEventSubscriber {
+abstract class DomainEventIngress implements InitializableDomainEventSubscriber {
/**
* @var string[]
@@ -153,7 +152,7 @@ abstract class EventIngressBase implements InitializableDomainEventSubscriber {
if ( !$this->eventTypes ) {
throw new LogicException(
- 'Subclassed of EventIngressBase must either override ' .
+ 'Subclassed of DomainEventIngress must either override ' .
'registerListeners or provide a list of event types via ' .
'initSubscriber() or initEvents().'
);
@@ -167,4 +166,6 @@ abstract class EventIngressBase implements InitializableDomainEventSubscriber {
}
/** @deprecated temporary alias, remove before 1.44 release (T389033) */
-class_alias( EventIngressBase::class, 'MediaWiki\DomainEvent\EventSubscriberBase' );
+class_alias( DomainEventIngress::class, 'MediaWiki\DomainEvent\EventSubscriberBase' );
+/** @deprecated temporary alias, remove before 1.44 release (T389033) */
+class_alias( DomainEventIngress::class, 'MediaWiki\DomainEvent\EventIngressBase' );
diff --git a/includes/DomainEvent/DomainEventSource.php b/includes/DomainEvent/DomainEventSource.php
index d440705f528f..2b8b2e5a2a8f 100644
--- a/includes/DomainEvent/DomainEventSource.php
+++ b/includes/DomainEvent/DomainEventSource.php
@@ -5,7 +5,6 @@ namespace MediaWiki\DomainEvent;
* Service object for registering listeners for domain events.
*
* @since 1.44
- * @unstable until 1.45
*/
interface DomainEventSource {
diff --git a/includes/DomainEvent/DomainEventSubscriber.php b/includes/DomainEvent/DomainEventSubscriber.php
index 93778de5a000..6009bcee9b04 100644
--- a/includes/DomainEvent/DomainEventSubscriber.php
+++ b/includes/DomainEvent/DomainEventSubscriber.php
@@ -7,9 +7,8 @@ namespace MediaWiki\DomainEvent;
* related event listeners.
*
* @since 1.44
- * @stable to type
* @note Extensions should not implement this interface directly but should
- * extend EventIngressBase.
+ * extend DomainEventIngress.
*/
interface DomainEventSubscriber {
diff --git a/includes/DomainEvent/InitializableDomainEventSubscriber.php b/includes/DomainEvent/InitializableDomainEventSubscriber.php
index 64fdff11d0ba..17f09f662fd9 100644
--- a/includes/DomainEvent/InitializableDomainEventSubscriber.php
+++ b/includes/DomainEvent/InitializableDomainEventSubscriber.php
@@ -8,7 +8,6 @@ namespace MediaWiki\DomainEvent;
*
* This is useful when constructing an DomainEventSubscriber from an object spec.
*
- * @since 1.44
* @internal for use by DomainEventSubscriber
*/
interface InitializableDomainEventSubscriber extends DomainEventSubscriber {
diff --git a/includes/Output/OutputPage.php b/includes/Output/OutputPage.php
index 0a33bb608ccd..80fd513cc61b 100644
--- a/includes/Output/OutputPage.php
+++ b/includes/Output/OutputPage.php
@@ -2102,6 +2102,7 @@ class OutputPage extends ContextSource {
* @deprecated since 1.44, use ::getMetadata()->setRevisionTimestamp(...)
*/
public function setRevisionTimestamp( $timestamp ) {
+ wfDeprecated( __METHOD__, '1.44' );
$previousValue = $this->metadata->getRevisionTimestamp();
$this->metadata->setRevisionTimestamp( $timestamp );
return $previousValue;
diff --git a/includes/ResourceLoader/ResourceLoaderEventIngress.php b/includes/ResourceLoader/ResourceLoaderEventIngress.php
index aa1711bcff29..2ae9800afddd 100644
--- a/includes/ResourceLoader/ResourceLoaderEventIngress.php
+++ b/includes/ResourceLoader/ResourceLoaderEventIngress.php
@@ -2,7 +2,7 @@
namespace MediaWiki\ResourceLoader;
-use MediaWiki\DomainEvent\EventIngressBase;
+use MediaWiki\DomainEvent\DomainEventIngress;
use MediaWiki\Page\Event\PageDeletedEvent;
use MediaWiki\Page\Event\PageDeletedListener;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
@@ -18,7 +18,7 @@ use Wikimedia\Rdbms\LBFactory;
* @internal
*/
class ResourceLoaderEventIngress
- extends EventIngressBase
+ extends DomainEventIngress
implements PageRevisionUpdatedListener, PageDeletedListener
{
diff --git a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
index bd0c794e142c..9a5f844d92d1 100644
--- a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
+++ b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
@@ -294,18 +294,6 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
}
/**
- * Controls how the parser cache is used.
- *
- * @param bool $read Whether we should look for cached output before parsing
- * @param bool $write Whether we should cache output after parsing
- */
- public function setUseParserCache( bool $read, bool $write ) {
- $this->parserOutputAccessOptions =
- ( $read ? 0 : ParserOutputAccess::OPT_FORCE_PARSE ) |
- ( $write ? 0 : ParserOutputAccess::OPT_NO_UPDATE_CACHE );
- }
-
- /**
* Determine whether stashing should be applied.
*
* @param bool $stash
diff --git a/includes/Rest/i18n/ar.json b/includes/Rest/i18n/ar.json
index db780db3ae9f..7737778374e2 100644
--- a/includes/Rest/i18n/ar.json
+++ b/includes/Rest/i18n/ar.json
@@ -141,5 +141,6 @@
"rest-schema-desc-mock-desc": "وصف وهمي.",
"rest-schema-desc-revision-metadata": "مراجعة بيانات التعريف",
"rest-schema-desc-media-file": "معلومات حول الملف",
+ "rest-schema-desc-page-history": "تاريخ مراجعة الصفحة",
"rest-schema-desc-search-results": "نتائج البحث"
}
diff --git a/includes/Rest/i18n/gl.json b/includes/Rest/i18n/gl.json
index 54c07b691cca..ee2bdc9ab2eb 100644
--- a/includes/Rest/i18n/gl.json
+++ b/includes/Rest/i18n/gl.json
@@ -87,6 +87,14 @@
"rest-unsupported-language-conversion": "Conversión de lingua non compatible: $1 a $2",
"rest-unknown-content-model": "Modelo de contido descoñecido: $1",
"rest-page-bundle-validation-error": "PageBundle non coincide con contentVersion: $1",
+ "rest-module": "Módulo",
+ "rest-module-default": "Módulo predeterminado",
+ "rest-module-extra-routes-title": "API REST de MediaWiki",
+ "rest-module-extra-routes-desc": "Extremos REST non asociados a ningún módulo",
+ "rest-module-specs.v0-title": "Especificacións",
+ "rest-module-specs.v0-desc": "Módulo de autodocumentación que proporciona descubrimento, especificacións e esquemas para todos os módulos dispoñibles.",
+ "rest-module-content.v1-title": "Contido da páxina",
+ "rest-module-content.v1-desc": "Ofrece acceso ao contido e metadatos das páxinas e revisións",
"rest-param-desc-revision-id": "Identificador da revisión",
"rest-param-desc-source": "Contido da páxina no formato especificado pola propiedade content_model"
}
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index f82bb3598935..68ec604820dd 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -2793,9 +2793,6 @@ return [
),
LoggerFactory::getProvider(),
- // UserBlockConstraint
- $services->getPermissionManager(),
-
// EditFilterMergedContentHookConstraint
$services->getHookContainer(),
@@ -2805,7 +2802,7 @@ return [
// SpamRegexConstraint
$services->getSpamChecker(),
- // UserRateLimitConstraint
+ // LinkPurgeRateLimitConstraint
$services->getRateLimiter()
);
},
diff --git a/includes/Storage/DerivedPageDataUpdater.php b/includes/Storage/DerivedPageDataUpdater.php
index e8e596230585..83d68b309664 100644
--- a/includes/Storage/DerivedPageDataUpdater.php
+++ b/includes/Storage/DerivedPageDataUpdater.php
@@ -26,7 +26,6 @@ use MediaWiki\ChangeTags\ChangeTags;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\Content;
-use MediaWiki\Content\ContentHandler;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Transform\ContentTransformer;
use MediaWiki\Deferred\DeferrableUpdate;
@@ -36,11 +35,9 @@ use MediaWiki\Deferred\RefreshSecondaryDataUpdate;
use MediaWiki\Deferred\SiteStatsUpdate;
use MediaWiki\DomainEvent\DomainEventDispatcher;
use MediaWiki\Edit\PreparedEdit;
-use MediaWiki\Exception\MWUnknownContentModelException;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\JobQueue\JobQueueGroup;
-use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob;
use MediaWiki\JobQueue\Jobs\ParsoidCachePrewarmJob;
use MediaWiki\Language\Language;
use MediaWiki\MainConfigNames;
@@ -160,11 +157,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
private $articleCountMethod;
/**
- * @var bool see $wgRCWatchCategoryMembership
- */
- private $rcWatchCategoryMembership = false;
-
- /**
* Stores (most of) the $options parameter of prepareUpdate().
* @see prepareUpdate()
*
@@ -535,14 +527,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
}
/**
- * @param bool $rcWatchCategoryMembership
- * @see $wgRCWatchCategoryMembership
- */
- public function setRcWatchCategoryMembership( $rcWatchCategoryMembership ) {
- $this->rcWatchCategoryMembership = $rcWatchCategoryMembership;
- }
-
- /**
* @return Title
*/
private function getTitle() {
@@ -738,16 +722,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
return $this->getRawSlot( $role )->getContent();
}
- /**
- * @param string $role slot role name
- * @return ContentHandler
- * @throws MWUnknownContentModelException
- */
- private function getContentHandler( $role ): ContentHandler {
- return $this->contentHandlerFactory
- ->getContentHandler( $this->getRawSlot( $role )->getModel() );
- }
-
private function usePrimary(): bool {
// TODO: can we just set a flag to true in prepareContent()?
return $this->wikiPage->wasLoadedFrom( IDBAccessObject::READ_LATEST );
@@ -1591,24 +1565,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
'defer' => DeferredUpdates::POSTSEND
] );
- // TODO: MCR: check if *any* changed slot supports categories!
- if ( $this->rcWatchCategoryMembership
- && $this->getContentHandler( SlotRecord::MAIN )->supportsCategories() === true
- && $event->isNominalContentChange()
- && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE )
- ) {
- // Note: jobs are pushed after deferred updates, so the job should be able to see
- // the recent change entry (also done via deferred updates) and carry over any
- // bot/deletion/IP flags, ect.
- $this->jobQueueGroup->lazyPush(
- CategoryMembershipChangeJob::newSpec(
- $this->getTitle(),
- $this->revision->getTimestamp(),
- $event->hasCause( PageUpdater::CAUSE_IMPORT )
- )
- );
- }
-
$id = $this->getPageId();
$title = $this->getTitle();
$wikiPage = $this->getWikiPage();
diff --git a/includes/Storage/PageUpdaterFactory.php b/includes/Storage/PageUpdaterFactory.php
index 7bb77215d916..108bd2e3e8be 100644
--- a/includes/Storage/PageUpdaterFactory.php
+++ b/includes/Storage/PageUpdaterFactory.php
@@ -304,9 +304,6 @@ class PageUpdaterFactory {
$derivedDataUpdater->setLogger( $this->logger );
$derivedDataUpdater->setArticleCountMethod(
$this->options->get( MainConfigNames::ArticleCountMethod ) );
- $derivedDataUpdater->setRcWatchCategoryMembership(
- $this->options->get( MainConfigNames::RCWatchCategoryMembership )
- );
return $derivedDataUpdater;
}
diff --git a/includes/api/ApiMessageTrait.php b/includes/api/ApiMessageTrait.php
index 8f796101ce2b..9c2bd7575117 100644
--- a/includes/api/ApiMessageTrait.php
+++ b/includes/api/ApiMessageTrait.php
@@ -67,6 +67,7 @@ trait ApiMessageTrait {
'importuploaderrorpartial' => 'partialupload',
'importuploaderrorsize' => 'filetoobig',
'importuploaderrortemp' => 'notempdir',
+ 'ipb-block-not-found' => 'alreadyblocked',
'ipb_already_blocked' => 'alreadyblocked',
'ipb_blocked_as_range' => 'blockedasrange',
'ipb_cant_unblock' => 'cantunblock',
diff --git a/includes/api/i18n/ja.json b/includes/api/i18n/ja.json
index 7a7b7cca92f3..35e9fadef31c 100644
--- a/includes/api/i18n/ja.json
+++ b/includes/api/i18n/ja.json
@@ -27,12 +27,14 @@
"Yamagata Yusuke",
"Yusuke1109",
"Yuukin0248",
+ "もなー(偽物)",
"ネイ"
]
},
"apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|ドキュメンテーション]]\n* [[mw:Special:MyLanguage/API:Etiquette|エチケットと使用ガイドライン]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api.lists.wikimedia.org/ メーリングリスト]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/ API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグと要望]\n</div>\n<strong>状態:</strong> MediaWiki APIは、サポートおよび更新が恒常的に続けられている、安定した多機能インターフェースです。時折大きな仕様変更が適用されることもありますが、[https://lists.wikimedia.org/hyperkitty/list/mediawiki-api-announce@lists.wikimedia.org/ the mediawiki-api-announce メーリングリスト]を購読すると適時にアップデート通知を受け取ることができます。\n\n<strong>リクエストエラー:</strong> 非適合形式でAPIにリクエストに送られた場合、\"MediaWiki-API-Error\"のキーを含むHTTPヘッダーが返され、レスポンスのヘッダー値とエラーコード値が同値になります。詳細は、[[mw:Special:MyLanguage/API:Errors_and_warnings|API:エラーと警告]]をご覧ください。\n\n<p class=\"mw-apisandbox-link\"><strong>テスト:</strong> [[Special:ApiSandbox|APIサンドボックス]]を使用すると、容易にAPIリクエストのテストが可能です。</p>",
"apihelp-main-param-action": "実行する操作。",
"apihelp-main-param-format": "出力フォーマット。",
+ "apihelp-main-param-maxlag": "最大ラグは、MediaWiki がデータベース複製クラスターにインストールされている場合に使用できます。サイトの複製ラグをさらに引き起こすアクションを防ぐために、このパラメータを使用すると、複製ラグが指定値より小さくなるまでクライアントを待機させることができます。ラグが大きすぎる場合は、エラー コード<samp>maxlag</samp>が返され、 <samp>Waiting for $host: $lag seconds lagged</samp>のようなメッセージが表示されます。<br />詳細については、[[mw:Special:MyLanguage/Manual:Maxlag_parameter|マニュアル: Maxlag パラメータ]] を参照してください。",
"apihelp-main-param-smaxage": "<code>s-maxage</code>のHTTPキャッシュコントロールヘッダーをこの秒数に指定します。エラーはキャッシュされません。",
"apihelp-main-param-maxage": "<code>max-age</code>のHTTPキャッシュコントロールヘッダーをこの秒数に指定します。エラーはキャッシュされません。",
"apihelp-main-param-assert": "<kbd>user</kbd> に設定した場合は利用者がログインしていること(臨時利用者としてログインしている場合も含む)を、 <kbd>anon</kbd> に設定した場合はログインして<em>いない</em>ことを、<kbd>bot</kbd> の場合はボット利用者権限があることを検証します。",
@@ -52,6 +54,7 @@
"apihelp-main-param-errorlang": "警告およびエラーで使用する言語。 <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> は言語コードの一覧を返します。 <kbd>content</kbd> を指定するとこのウィキのコンテンツ言語、<kbd>uselang</kbd> を指定すると <var>uselang</var> パラメーターと同じ値を使用します。",
"apihelp-main-param-errorsuselocal": "指定された場合、エラーテキストは{{ns:MediaWiki}}名前空間からローカルにカスタマイズされたメッセージを使用します。",
"apihelp-block-summary": "利用者をブロックします。",
+ "apihelp-block-param-id": "変更するブロックID。",
"apihelp-block-param-user": "ブロックする利用者。",
"apihelp-block-param-userid": "代わりに <kbd>$1user=#<var>ID</var></kbd> を指定してください。",
"apihelp-block-param-expiry": "有効期限。相対的 (例: <kbd>5 months</kbd> または <kbd>2 weeks</kbd>) または絶対的 (e.g. <kbd>2014-09-18T12:34:56Z</kbd>) どちらでも構いません。<kbd>infinite</kbd>, <kbd>indefinite</kbd>, もしくは <kbd>never</kbd> と設定した場合, 無期限ブロックとなります。",
@@ -63,18 +66,26 @@
"apihelp-block-param-hidename": "ブロック記録から利用者名を秘匿します。(<code>hideuser</code> 権限が必要です)",
"apihelp-block-param-allowusertalk": "自身のトークページの編集を許可する (<var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var> に依存)。",
"apihelp-block-param-reblock": "その利用者がすでにブロックされている場合、ブロックを上書きします。",
+ "apihelp-block-param-newblock": "利用者がすでにブロックされている場合でも、別のブロックを追加します。",
"apihelp-block-param-watchuser": "その利用者またはIPアドレスの利用者ページとトークページをウォッチします。",
"apihelp-block-param-watchlistexpiry": "ウォッチリストの有効期限のタイムスタンプ。現在の有効期限を変更せずそのままにするには、このパラメーターを完全に省略します。",
"apihelp-block-param-tags": "ブロック記録の項目に適用する変更タグ。",
"apihelp-block-param-partial": "サイト全体ではなく特定のページまたは名前空間での編集をブロックします。",
"apihelp-block-param-pagerestrictions": "利用者が編集できないようにするページのタイトルのリスト。<var>partial</var> に true が設定されている場合のみ適用します。",
"apihelp-block-param-namespacerestrictions": "利用者が編集できないようにする名前空間のID。<var>partial</var> に true が設定されている場合のみ適用します。",
+ "apihelp-block-param-actionrestrictions": "利用者に実行させない操作のリスト。<var>partial</var> が true に設定されている場合のみ適用されます。",
"apihelp-block-example-ip-simple": "IPアドレス <kbd>192.0.2.5</kbd> を何らかの理由で3日ブロックする。",
"apihelp-block-example-user-complex": "利用者 <kbd>Vandal</kbd> を何らかの理由で無期限ブロックし、新たなアカウント作成とメールの送信を禁止する。",
"apihelp-changeauthenticationdata-summary": "現在の利用者の認証データを変更します。",
"apihelp-changeauthenticationdata-example-password": "現在の利用者のパスワードを <kbd>ExamplePassword</kbd> に変更する。",
"apihelp-changecontentmodel-summary": "ページのコンテンツモデルを変更します。",
+ "apihelp-changecontentmodel-param-title": "コンテンツモデルを変更するページ名。<var>$1pageid</var>と併用することはできません。",
+ "apihelp-changecontentmodel-param-pageid": "コンテンツモデルを変更するページID。<var>$1title</var> とは併用できません。",
+ "apihelp-changecontentmodel-param-summary": "編集の概要と記録エントリの理由",
+ "apihelp-changecontentmodel-param-tags": "記録エントリに適用するタグを変更して編集します。",
"apihelp-changecontentmodel-param-model": "新しいコンテンツのコンテンツモデル。",
+ "apihelp-changecontentmodel-param-bot": "コンテンツモデルの変更をボット フラグでマークします。",
+ "apihelp-changecontentmodel-example": "メインページが<code>text</code>コンテンツモデルを持つよう変更する",
"apihelp-checktoken-summary": "<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd> のトークンの妥当性を確認します。",
"apihelp-checktoken-param-type": "調べるトークンの種類。",
"apihelp-checktoken-param-token": "調べるトークン。",
diff --git a/includes/api/i18n/pt-br.json b/includes/api/i18n/pt-br.json
index 9dba97e178e7..e9cb4c0d51a6 100644
--- a/includes/api/i18n/pt-br.json
+++ b/includes/api/i18n/pt-br.json
@@ -32,45 +32,52 @@
"apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:Etiquette|Etiqueta & diretrizes de uso]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api.lists.wikimedia.org/ Lista de discussão]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/ Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> A API do MediaWiki é uma interface madura e estável que é ativamente suportada e aprimorada. Por mais que tentemos evitar, ocasionalmente talvez precisemos fazer mudanças estruturais; inscreva-se na [https://lists.wikimedia.org/hyperkitty/list/mediawiki-api-announce@lists.wikimedia.org/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].</p>",
"apihelp-main-param-action": "Qual ação executar.",
"apihelp-main-param-format": "O formato da saída.",
- "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] para mais informações.",
- "apihelp-main-param-smaxage": "Define o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
- "apihelp-main-param-maxage": "Define o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
- "apihelp-main-param-assert": "Verifique se o usuário está logado se configurado para <kbd>user</kbd>, <em>not</em> logado se definido como <kbd>anon</kbd> ou tem o direito do usuário do bot se <kbd>bot</kbd>.",
- "apihelp-main-param-assertuser": "Verificar que o usuário atual é o utilizador nomeado.",
+ "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.",
+ "apihelp-main-param-smaxage": "Definir o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
+ "apihelp-main-param-maxage": "Definir o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
+ "apihelp-main-param-assert": "Verificar se o usuário está logado (incluindo possivelmente como usuário temporário) quando definido como <kbd>user</kbd>, se <em>não</em> está logado quando definido como <kbd>anon</kbd>, ou se tem o direito de usuário de robô quando definido como <kbd>bot</kbd>.",
+ "apihelp-main-param-assertuser": "Verificar se o usuário atual é o usuário nomeado.",
"apihelp-main-param-requestid": "Qualquer valor dado aqui será incluído na resposta. Pode ser usado para distinguir requisições.",
- "apihelp-main-param-servedby": "Incluir nos resultados o nome do servidor que serviu o pedido.",
- "apihelp-main-param-curtimestamp": "Inclui o timestamp atual no resultado.",
- "apihelp-main-param-responselanginfo": "Inclua os idiomas usados para <var>uselang</var> e <var>errorlang</var> no resultado.",
- "apihelp-main-param-origin": "Ao acessar a API usando uma solicitação AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda solicitação ''pre-flight'', sendo portanto parte do URI da solicitação (ao invés do corpo do POST).\n\nPara solicitações autenticadas, isto deve corresponder a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida (''whitelisted''), os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara solicitações não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos para usuários tornar-se-ão restritos.",
- "apihelp-main-param-uselang": "Linguagem a ser usada para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.",
- "apihelp-main-param-errorformat": "Formato a ser usado aviso e saída de texto de erro.\n; Texto simples: Texto wiki com tags HTML removidas e entidades substituídas.\n; Wikitext: Unparsed wikitext. \n; html: HTML.\n; Bruto: chave e parâmetros da mensagem.\n; Nenhum: sem saída de texto, apenas os códigos de erro.\n; Bc: Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> são ignorados.",
+ "apihelp-main-param-servedby": "Incluir nos resultados o hostname que serviu a requisição.",
+ "apihelp-main-param-curtimestamp": "Incluir no resultado o timestamp atual.",
+ "apihelp-main-param-responselanginfo": "Incluir no resultado os idiomas usados para <var>uselang</var> e <var>errorlang</var>.",
+ "apihelp-main-param-origin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nPara requisições autenticadas, isto deve corresponder exatamente a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida, os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara requisições não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos de usuário tornar-se-ão restritos.",
+ "apihelp-main-param-crossorigin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS) e um provedor de sessão seguro contra ataques de falsificação de requisição entre websites (CSRF), como o provedor OAuth, use isto em vez de <code>origin=*</code> para tornar a requisição autenticada (ou seja, evitar que ela não esteja logada). Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nNote que a maioria dos provedores de sessão, incluindo sessões padrões baseadas em cookies, não são compatíveis com CORS autenticado e, portanto, não podem ser utilizadas com este parâmetro.",
+ "apihelp-main-param-uselang": "Idioma a ser usado para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.",
+ "apihelp-main-param-variant": "Variante do idioma. Funciona somente se o idioma-base for compatível com conversão para suas variantes.",
+ "apihelp-main-param-errorformat": "Formato a ser usado para textos de avisos e erros na saída",
+ "apihelp-main-paramvalue-errorformat-plaintext": "Wikitexto com marcações HTML removidas e entidades substituidas.",
"apihelp-main-paramvalue-errorformat-wikitext": "Wikitexto não analisado.",
"apihelp-main-paramvalue-errorformat-raw": "Chave e parâmetros da mensagem.",
"apihelp-main-paramvalue-errorformat-none": "Sem saída de texto, apenas os códigos de erro.",
"apihelp-main-paramvalue-errorformat-bc": "Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> serão ignorados.",
- "apihelp-main-param-errorlang": "Linguagem a utilizar para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou especifique <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.",
- "apihelp-main-param-errorsuselocal": "Se for dado, os textos de erro usarão mensagens customizadas localmente a partir do espaço nominal {{ns: MediaWiki}}.",
+ "apihelp-main-param-errorlang": "Idioma a ser utilizado para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.",
+ "apihelp-main-param-errorsuselocal": "Se fornecido, os textos de erro usarão mensagens personalizadas localmente a partir do espaço nominal {{ns:MediaWiki}}.",
+ "apihelp-acquiretempusername-summary": "Adquirir um nome de usuário temporário e armazená-lo na sessão atual caso a criação de contas temporárias esteja ativada e o usuário atual não estiver logado. Se um nome já foi armazenado, retorna esse nome.",
+ "apihelp-acquiretempusername-extended-description": "Se o usuário posteriormente realizar uma ação que resulte na criação de conta temporária, o nome de usuário armazenado será utilizado para a conta criada. Ele também poderá ser utilizado em previsões. Entretanto, a conta ainda não terá sido criada, e o nome não estará visível a outros usuários.",
"apihelp-block-summary": "Bloquear um usuário.",
- "apihelp-block-param-user": "Usuário para bloquear.",
- "apihelp-block-param-userid": "Especifique <kbd>$1user=#<var>ID</var></kbd>.",
- "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (por exemplo <kbd>5 meses</kbd> ou <kbd>2 semanas</kbd>) ou absoluto (por exemplo <kbd>2014-09-18T12:34:56Z</kbd>). Se definido para <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.",
+ "apihelp-block-param-id": "O ID de bloqueio a ser modificado.",
+ "apihelp-block-param-user": "Usuário a ser bloqueado.",
+ "apihelp-block-param-userid": "Ao invés, especifique <kbd>$1user=#<var>ID</var></kbd>.",
+ "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (p. ex., <kbd>5 months</kbd> ou <kbd>2 weeks</kbd>) ou absoluto (p. ex., <kbd>2014-09-18T12:34:56Z</kbd>). Se definido como <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.",
"apihelp-block-param-reason": "Razão do bloqueio.",
- "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja. desativa edições anônimas para este endereço IP).",
+ "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja, desativa edições anônimas para este endereço IP, incluindo edições de contas temporárias).",
"apihelp-block-param-nocreate": "Prevenir a criação de conta.",
- "apihelp-block-param-autoblock": "Bloquear automaticamente o endereço IP usado e quaisquer endereços IPs subsequentes que tentarem acessar a partir deles.",
+ "apihelp-block-param-autoblock": "Bloquear automaticamente o último endereço IP utilizado e quaisquer endereços IP subsequentes a partir dos quais houver tentativa de login.",
"apihelp-block-param-noemail": "Impedir que o usuário envie e-mails através da wiki. (Requer o direito <code>blockemail</code>).",
- "apihelp-block-param-hidename": "Oculta o nome do usuário do ''log'' de bloqueio. (Requer o direito <code>hideuser</code>).",
+ "apihelp-block-param-hidename": "Oculta o nome do usuário dos registros de bloqueio. (Requer o direito <code>hideuser</code>).",
"apihelp-block-param-allowusertalk": "Permitir que o usuário edite sua própria página de discussão (depende de <var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var>).",
- "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado, sobrescrever o bloqueio existente.",
+ "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado por um único bloqueio, sobrescrever o bloqueio existente. Se o usuário estiver com múltiplos bloqueios, isso falhará; utilize o parâmetro <var>id</var> para especificar qual bloqueio será sobrescrito.",
+ "apihelp-block-param-newblock": "Adicionar outro bloqueio mesmo se o usuário já estiver bloqueado.",
"apihelp-block-param-watchuser": "Vigiar as páginas de usuário e de discussão, do usuário ou do endereço IP.",
"apihelp-block-param-watchlistexpiry": "Carimbo de data/hora de expiração da lista de páginas vigiadas. Omita esse parâmetro inteiramente para manter inalterada a validade atual.",
"apihelp-block-param-tags": "As tags de alteração a serem aplicadas à entrada no registro de bloqueio.",
- "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o site.",
- "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).",
- "apihelp-block-param-namespacerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).",
- "apihelp-block-param-actionrestrictions": "Lista de ações para bloquear a execução do usuário. Aplica-se apenas quando <var>parcial</var> é definido como verdadeiro.",
- "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com a razão;",
- "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com a razão e o impedir de criar nova conta e de enviar e-mails.",
+ "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o website.",
+ "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-param-namespacerestrictions": "Lista de IDs de espaços nominais que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-param-actionrestrictions": "Lista de ações que o bloqueio impedirá o usuário de executar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com um motivo.",
+ "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com um motivo, e impedi-lo de criar nova conta e de enviar e-mails.",
"apihelp-changeauthenticationdata-summary": "Alterar os dados de autenticação para o usuário atual.",
"apihelp-changeauthenticationdata-example-password": "Tenta alterar a senha do usuário atual para <kbd>ExamplePassword</kbd>.",
"apihelp-changecontentmodel-summary": "Alterar o modelo de conteúdo de uma página",
@@ -88,7 +95,7 @@
"apihelp-checktoken-example-simple": "Testa a validade de um token <kbd>csrf</kbd>.",
"apihelp-clearhasmsg-summary": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.",
"apihelp-clearhasmsg-example-1": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.",
- "apihelp-clientlogin-summary": "Faça o login no wiki usando o fluxo interativo.",
+ "apihelp-clientlogin-summary": "Entrar na wiki usando o fluxo interativo.",
"apihelp-clientlogin-example-login": "Comeca o processo de logar na wiki como usuário <kbd>Exemple</kbd> com a senha <kbd>ExamplePassword</kbd>.",
"apihelp-clientlogin-example-login2": "Continuar efetuando login após uma resposta <samp>UI</samp> para autenticação de dois fatores, fornecendo um <var>OATHToken</var> de <kbd>987654</ kbd>.",
"apihelp-compare-summary": "Obter a diferença entre duas páginas.",
@@ -197,7 +204,7 @@
"apihelp-expandtemplates-param-prop": "Quais peças de informação obter.\n\nNote que se nenhum valor for selecionado, o resultado conterá o texto wiki, mas o resultado será em um formato obsoleto.",
"apihelp-expandtemplates-paramvalue-prop-wikitext": "O texto wiki expandido.",
"apihelp-expandtemplates-paramvalue-prop-categories": "Quaisquer categorias presentes na entrada que não estão representadas na saída wikitext.",
- "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no texto wiki.",
+ "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no wikitexto.",
"apihelp-expandtemplates-paramvalue-prop-volatile": "Se a saída é volátil e não deve ser reutilizada em outro lugar dentro da página.",
"apihelp-expandtemplates-paramvalue-prop-ttl": "O tempo máximo após o qual os caches do resultado devem ser invalidados.",
"apihelp-expandtemplates-paramvalue-prop-modules": "Quaisquer módulos ResourceLoader que as funções do analisador solicitaram foram adicionados à saída. Contudo, <kbd>jsconfigvars</kbd> ou <kbd>encodedjsconfigvars</kbd> devem ser solicitados em conjunto com <kbd>modules</kbd>.",
@@ -370,13 +377,13 @@
"apihelp-parse-param-prop": "Qual pedaço de informação obter:",
"apihelp-parse-paramvalue-prop-text": "Fornece o texto analisado do texto wiki.",
"apihelp-parse-paramvalue-prop-langlinks": "Fornece os links de idiomas do texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-categorieshtml": "Fornece a versão HTML das categorias.",
"apihelp-parse-paramvalue-prop-links": "Fornece os links internos do texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-templates": "Fornece a predefinição no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-externallinks": "Fornece os links externos no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-templates": "Fornece as predefinições no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-externallinks": "Fornece as ligações externas no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-revid": "Adiciona o ID da revisão da página analisada.",
"apihelp-parse-paramvalue-prop-displaytitle": "Adiciona o título do texto wiki analisado.",
"apihelp-parse-paramvalue-prop-subtitle": "Adiciona o subtítulo de página da página a que foi feita a análise sintática.",
@@ -386,9 +393,9 @@
"apihelp-parse-paramvalue-prop-jsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página. Para aplicar, use <code>mw.config.set()</code>.",
"apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página como uma string JSON.",
"apihelp-parse-paramvalue-prop-indicators": "Fornece o HTML de indicadores de ''status'' de página utilizados na página.",
- "apihelp-parse-paramvalue-prop-iwlinks": "Fornece links interwiki no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-iwlinks": "Fornece ligações interwiki no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-wikitext": "Fornece o texto wiki original que foi analisado.",
- "apihelp-parse-paramvalue-prop-properties": "Fornece várias propriedades definidas no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-properties": "Fornece propriedades variadas definidas no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-limitreportdata": "Fornece o relatório limite de uma forma estruturada. Não informa dado, quando<var>$1disablelimitreport</var> está definido.",
"apihelp-parse-paramvalue-prop-limitreporthtml": "Retorna a versão HTML do relatório de limite. Não retorna dados quando <var>$1disablelimitreport</var> está definido.",
"apihelp-parse-paramvalue-prop-parsetree": "A árvore de análise XML do conteúdo da revisão (requer modelo de conteúdo <code>$1</code>)",
diff --git a/includes/block/BlockUser.php b/includes/block/BlockUser.php
index aa9ac9269fd4..3de0388d1611 100644
--- a/includes/block/BlockUser.php
+++ b/includes/block/BlockUser.php
@@ -587,7 +587,6 @@ class BlockUser {
}
$expectedTargetCount = 0;
- $priorBlock = null;
$priorBlocks = $this->getPriorBlocksForTarget();
if ( $this->blockToUpdate !== null ) {
@@ -601,7 +600,7 @@ class BlockUser {
} elseif ( $conflictMode === self::CONFLICT_NEW
&& $this->options->get( MainConfigNames::EnableMultiBlocks )
) {
- foreach ( $this->getPriorBlocksForTarget() as $priorBlock ) {
+ foreach ( $priorBlocks as $priorBlock ) {
if ( $block->equals( $priorBlock ) ) {
// Block settings are equal => user is already blocked
$this->logger->debug( 'placeBlockInternal: ' .
@@ -610,8 +609,10 @@ class BlockUser {
}
}
$expectedTargetCount = null;
+ $priorBlock = null;
$update = false;
} elseif ( !$priorBlocks ) {
+ $priorBlock = null;
$update = false;
} else {
// Reblock only if the caller wants so
diff --git a/includes/block/DatabaseBlockStore.php b/includes/block/DatabaseBlockStore.php
index c33157b58157..20f662e044d5 100644
--- a/includes/block/DatabaseBlockStore.php
+++ b/includes/block/DatabaseBlockStore.php
@@ -1044,6 +1044,7 @@ class DatabaseBlockStore {
$targetUserName = (string)$target;
$targetUserId = $target->getUserIdentity()->getId( $this->wikiId );
$targetConds = [ 'bt_user' => $targetUserId ];
+ $targetLockKey = $dbw->getDomainID() . ':block:u:' . $targetUserId;
} else {
$targetAddress = (string)$target;
$targetUserName = null;
@@ -1052,6 +1053,8 @@ class DatabaseBlockStore {
'bt_address' => $targetAddress,
'bt_auto' => $isAuto,
];
+ $targetLockKey = $dbw->getDomainID() . ':block:' .
+ ( $isAuto ? 'a' : 'i' ) . ':' . $targetAddress;
}
$condsWithCount = $targetConds;
@@ -1059,6 +1062,15 @@ class DatabaseBlockStore {
$condsWithCount['bt_count'] = $expectedTargetCount;
}
+ $dbw->lock( $targetLockKey, __METHOD__ );
+ $func = __METHOD__;
+ $dbw->onTransactionCommitOrIdle(
+ static function () use ( $dbw, $targetLockKey, $func ) {
+ $dbw->unlock( $targetLockKey, "$func.closure" );
+ },
+ __METHOD__
+ );
+
// This query locks the index gap when the target doesn't exist yet,
// so there is a risk of throttling adjacent block insertions,
// especially on small wikis which have larger gaps. If this proves to
@@ -1076,6 +1088,7 @@ class DatabaseBlockStore {
->select( [ 'bt_id', 'bt_count' ] )
->from( 'block_target' )
->where( $targetConds )
+ ->forUpdate()
->caller( __METHOD__ )
->fetchResultSet();
if ( $res->numRows() > 1 ) {
diff --git a/includes/composer/ComposerVendorHtaccessCreator.php b/includes/composer/ComposerVendorHtaccessCreator.php
index e2a079b021d5..ef835fa1d898 100644
--- a/includes/composer/ComposerVendorHtaccessCreator.php
+++ b/includes/composer/ComposerVendorHtaccessCreator.php
@@ -40,6 +40,8 @@ class ComposerVendorHtaccessCreator {
return;
}
- file_put_contents( $fname, "Require all denied\n" );
+ file_put_contents( $fname,
+ "Require all denied\n" .
+ "Satisfy All\n" );
}
}
diff --git a/includes/deferred/LinksUpdate/CategoryLinksTable.php b/includes/deferred/LinksUpdate/CategoryLinksTable.php
index 151566c9f408..bee5b26518ef 100644
--- a/includes/deferred/LinksUpdate/CategoryLinksTable.php
+++ b/includes/deferred/LinksUpdate/CategoryLinksTable.php
@@ -14,6 +14,7 @@ use MediaWiki\MainConfigNames;
use MediaWiki\Page\PageReferenceValue;
use MediaWiki\Page\WikiPageFactory;
use MediaWiki\Parser\ParserOutput;
+use MediaWiki\Parser\ParserOutputLinkTypes;
use MediaWiki\Parser\Sanitizer;
use MediaWiki\Storage\NameTableStore;
use MediaWiki\Title\NamespaceInfo;
@@ -142,8 +143,10 @@ class CategoryLinksTable extends TitleLinksTable {
$this->newLinks = [];
$sourceTitle = Title::castFromPageIdentity( $this->getSourcePage() );
$sortKeyInputs = [];
- foreach ( $parserOutput->getCategoryNames() as $name ) {
- $sortKey = $parserOutput->getCategorySortKey( $name );
+ foreach (
+ $parserOutput->getLinkList( ParserOutputLinkTypes::CATEGORY )
+ as [ 'link' => $targetTitle, 'sort' => $sortKey ]
+ ) {
'@phan-var string $sortKey'; // sort key will never be null
if ( $sortKey == '' ) {
@@ -160,7 +163,8 @@ class CategoryLinksTable extends TitleLinksTable {
// categories, causing T27254.
$sortKeyPrefix = mb_strcut( $sortKey, 0, 255 );
- $targetTitle = Title::makeTitle( NS_CATEGORY, $name );
+ $name = $targetTitle->getDBkey();
+ $targetTitle = Title::castFromLinkTarget( $targetTitle );
$this->languageConverter->findVariantLink( $name, $targetTitle, true );
// Ignore the returned text, DB key should be used for links (T328477).
$name = $targetTitle->getDBKey();
diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php
index faca5c529263..e5a3b1428525 100644
--- a/includes/diff/DifferenceEngine.php
+++ b/includes/diff/DifferenceEngine.php
@@ -1238,7 +1238,7 @@ class DifferenceEngine extends ContextSource {
$out->setRevisionId( $this->mNewid );
$out->setRevisionIsCurrent( $this->mNewRevisionRecord->isCurrent() );
- $out->setRevisionTimestamp( $this->mNewRevisionRecord->getTimestamp() );
+ $out->getMetadata()->setRevisionTimestamp( $this->mNewRevisionRecord->getTimestamp() );
$out->setArticleFlag( true );
if ( !$this->hookRunner->onArticleRevisionViewCustom(
diff --git a/includes/editpage/Constraint/AuthorizationConstraint.php b/includes/editpage/Constraint/AuthorizationConstraint.php
new file mode 100644
index 000000000000..73247160fa7b
--- /dev/null
+++ b/includes/editpage/Constraint/AuthorizationConstraint.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\EditPage\Constraint;
+
+use MediaWiki\Page\PageIdentity;
+use MediaWiki\Permissions\Authority;
+use MediaWiki\Permissions\PermissionStatus;
+use StatusValue;
+
+/**
+ * Verify authorization to edit the page (user rights, rate limits, blocks).
+ *
+ * @since 1.44
+ * @internal
+ */
+class AuthorizationConstraint implements IEditConstraint {
+
+ private PermissionStatus $status;
+
+ private Authority $performer;
+ private PageIdentity $target;
+ private bool $new;
+
+ public function __construct(
+ Authority $performer,
+ PageIdentity $target,
+ bool $new
+ ) {
+ $this->performer = $performer;
+ $this->target = $target;
+ $this->new = $new;
+ }
+
+ public function checkConstraint(): string {
+ $this->status = PermissionStatus::newEmpty();
+
+ if ( $this->new && !$this->performer->authorizeWrite( 'create', $this->target, $this->status ) ) {
+ return self::CONSTRAINT_FAILED;
+ }
+
+ if ( !$this->performer->authorizeWrite( 'edit', $this->target, $this->status ) ) {
+ return self::CONSTRAINT_FAILED;
+ }
+
+ return self::CONSTRAINT_PASSED;
+ }
+
+ public function getLegacyStatus(): StatusValue {
+ $statusValue = StatusValue::newGood();
+
+ if ( !$this->status->isGood() ) {
+ // Report the most specific errors first
+ if ( $this->status->isBlocked() ) {
+ $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
+ } elseif ( $this->status->isRateLimitExceeded() ) {
+ $statusValue->setResult( false, self::AS_RATE_LIMITED );
+ } elseif ( $this->status->getPermission() === 'create' ) {
+ $statusValue->setResult( false, self::AS_NO_CREATE_PERMISSION );
+ } elseif ( !$this->performer->isRegistered() ) {
+ $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
+ } else {
+ $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_LOGGED );
+ }
+ }
+
+ // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399
+ return $statusValue;
+ }
+
+}
diff --git a/includes/editpage/Constraint/ContentModelChangeConstraint.php b/includes/editpage/Constraint/ContentModelChangeConstraint.php
index 8ed2e56debde..2581ebab3c14 100644
--- a/includes/editpage/Constraint/ContentModelChangeConstraint.php
+++ b/includes/editpage/Constraint/ContentModelChangeConstraint.php
@@ -21,6 +21,7 @@
namespace MediaWiki\EditPage\Constraint;
use MediaWiki\Permissions\Authority;
+use MediaWiki\Permissions\PermissionStatus;
use MediaWiki\Title\Title;
use StatusValue;
@@ -28,6 +29,7 @@ use StatusValue;
* Verify user permissions if changing content model:
* Must have editcontentmodel rights
* Must be able to edit under the new content model
+ * Must not have exceeded the rate limit
*
* @since 1.36
* @internal
@@ -35,10 +37,11 @@ use StatusValue;
*/
class ContentModelChangeConstraint implements IEditConstraint {
+ private PermissionStatus $status;
+
private Authority $performer;
private Title $title;
private string $newContentModel;
- private string $result;
/**
* @param Authority $performer
@@ -56,45 +59,43 @@ class ContentModelChangeConstraint implements IEditConstraint {
}
public function checkConstraint(): string {
+ $this->status = PermissionStatus::newEmpty();
+
if ( $this->newContentModel === $this->title->getContentModel() ) {
// No change
- $this->result = self::CONSTRAINT_PASSED;
return self::CONSTRAINT_PASSED;
}
- if ( !$this->performer->isAllowed( 'editcontentmodel' ) ) {
- $this->result = self::CONSTRAINT_FAILED;
+ if ( !$this->performer->authorizeWrite( 'editcontentmodel', $this->title, $this->status ) ) {
return self::CONSTRAINT_FAILED;
}
- // Make sure the user can edit the page under the new content model too
+ // Make sure the user can edit the page under the new content model too.
+ // We rely on caching in UserAuthority to avoid bumping the rate limit counter twice.
$titleWithNewContentModel = clone $this->title;
$titleWithNewContentModel->setContentModel( $this->newContentModel );
-
- $canEditModel = $this->performer->authorizeWrite(
- 'editcontentmodel',
- $titleWithNewContentModel
- );
-
if (
- !$canEditModel
- || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel )
+ !$this->performer->authorizeWrite( 'editcontentmodel', $titleWithNewContentModel, $this->status )
+ || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel, $this->status )
) {
- $this->result = self::CONSTRAINT_FAILED;
return self::CONSTRAINT_FAILED;
}
- $this->result = self::CONSTRAINT_PASSED;
return self::CONSTRAINT_PASSED;
}
public function getLegacyStatus(): StatusValue {
$statusValue = StatusValue::newGood();
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ if ( !$this->status->isGood() ) {
+ if ( $this->status->isRateLimitExceeded() ) {
+ $statusValue->setResult( false, self::AS_RATE_LIMITED );
+ } else {
+ $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ }
}
+ // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399
return $statusValue;
}
diff --git a/includes/editpage/Constraint/EditConstraintFactory.php b/includes/editpage/Constraint/EditConstraintFactory.php
index 750c977d2312..e9fede9f256c 100644
--- a/includes/editpage/Constraint/EditConstraintFactory.php
+++ b/includes/editpage/Constraint/EditConstraintFactory.php
@@ -26,10 +26,8 @@ use MediaWiki\Context\IContextSource;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Language\Language;
-use MediaWiki\Linker\LinkTarget;
use MediaWiki\Logger\Spi;
use MediaWiki\MainConfigNames;
-use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\RateLimiter;
use MediaWiki\Permissions\RateLimitSubject;
use MediaWiki\Title\Title;
@@ -54,7 +52,6 @@ class EditConstraintFactory {
private ServiceOptions $options;
private Spi $loggerFactory;
- private PermissionManager $permissionManager;
private HookContainer $hookContainer;
private ReadOnlyMode $readOnlyMode;
private SpamChecker $spamRegexChecker;
@@ -74,7 +71,6 @@ class EditConstraintFactory {
*
* @param ServiceOptions $options
* @param Spi $loggerFactory
- * @param PermissionManager $permissionManager
* @param HookContainer $hookContainer
* @param ReadOnlyMode $readOnlyMode
* @param SpamChecker $spamRegexChecker
@@ -83,7 +79,6 @@ class EditConstraintFactory {
public function __construct(
ServiceOptions $options,
Spi $loggerFactory,
- PermissionManager $permissionManager,
HookContainer $hookContainer,
ReadOnlyMode $readOnlyMode,
SpamChecker $spamRegexChecker,
@@ -95,9 +90,6 @@ class EditConstraintFactory {
$this->options = $options;
$this->loggerFactory = $loggerFactory;
- // UserBlockConstraint
- $this->permissionManager = $permissionManager;
-
// EditFilterMergedContentHookConstraint
$this->hookContainer = $hookContainer;
@@ -107,7 +99,7 @@ class EditConstraintFactory {
// SpamRegexConstraint
$this->spamRegexChecker = $spamRegexChecker;
- // UserRateLimitConstraint
+ // LinkPurgeRateLimitConstraint
$this->rateLimiter = $rateLimiter;
}
@@ -163,21 +155,15 @@ class EditConstraintFactory {
/**
* @param RateLimitSubject $subject
- * @param string $oldModel
- * @param string $newModel
*
- * @return UserRateLimitConstraint
+ * @return LinkPurgeRateLimitConstraint
*/
- public function newUserRateLimitConstraint(
- RateLimitSubject $subject,
- string $oldModel,
- string $newModel
- ): UserRateLimitConstraint {
- return new UserRateLimitConstraint(
+ public function newLinkPurgeRateLimitConstraint(
+ RateLimitSubject $subject
+ ): LinkPurgeRateLimitConstraint {
+ return new LinkPurgeRateLimitConstraint(
$this->rateLimiter,
- $subject,
- $oldModel,
- $newModel
+ $subject
);
}
@@ -226,39 +212,4 @@ class EditConstraintFactory {
);
}
- /**
- * @param LinkTarget $title
- * @param User $user
- * @return UserBlockConstraint
- */
- public function newUserBlockConstraint(
- LinkTarget $title,
- User $user
- ): UserBlockConstraint {
- return new UserBlockConstraint(
- $this->permissionManager,
- $title,
- $user
- );
- }
-
- /**
- * @param User $performer
- * @param Title $title
- * @param bool $new
- * @return EditRightConstraint
- */
- public function newEditRightConstraint(
- User $performer,
- Title $title,
- bool $new
- ): EditRightConstraint {
- return new EditRightConstraint(
- $performer,
- $this->permissionManager,
- $title,
- $new
- );
- }
-
}
diff --git a/includes/editpage/Constraint/EditRightConstraint.php b/includes/editpage/Constraint/EditRightConstraint.php
deleted file mode 100644
index 47be037e4dad..000000000000
--- a/includes/editpage/Constraint/EditRightConstraint.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-namespace MediaWiki\EditPage\Constraint;
-
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\Title\Title;
-use MediaWiki\User\User;
-use StatusValue;
-
-/**
- * Verify user permissions:
- * Must have edit rights
- *
- * @since 1.36
- * @internal
- * @author DannyS712
- */
-class EditRightConstraint implements IEditConstraint {
-
- private User $performer;
- private PermissionManager $permManager;
- private Title $title;
- private string $result;
- private bool $new;
-
- /**
- * @param User $performer
- * @param PermissionManager $permManager
- * @param Title $title
- * @param bool $new
- */
- public function __construct(
- User $performer,
- PermissionManager $permManager,
- Title $title,
- bool $new
- ) {
- $this->performer = $performer;
- $this->permManager = $permManager;
- $this->title = $title;
- $this->new = $new;
- }
-
- public function checkConstraint(): string {
- if ( $this->new ) {
- // Check isn't simple enough to just repeat when getting the status
- if ( !$this->performer->authorizeWrite( 'create', $this->title ) ) {
- $this->result = (string)self::AS_NO_CREATE_PERMISSION;
- return self::CONSTRAINT_FAILED;
- }
- }
-
- // Check isn't simple enough to just repeat when getting the status
- // Prior to 1.41 this checked if the user had edit rights in general
- // instead of for the specific page in question.
- if ( !$this->permManager->userCan(
- 'edit',
- $this->performer,
- $this->title
- ) ) {
- $this->result = self::CONSTRAINT_FAILED;
- return self::CONSTRAINT_FAILED;
- }
-
- $this->result = self::CONSTRAINT_PASSED;
- return self::CONSTRAINT_PASSED;
- }
-
- public function getLegacyStatus(): StatusValue {
- $statusValue = StatusValue::newGood();
-
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- if ( !$this->performer->isRegistered() ) {
- $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
- } else {
- $statusValue->fatal( 'readonlytext' );
- $statusValue->value = self::AS_READ_ONLY_PAGE_LOGGED;
- }
- } elseif ( $this->result === (string)self::AS_NO_CREATE_PERMISSION ) {
- $statusValue->fatal( 'nocreatetext' );
- $statusValue->value = self::AS_NO_CREATE_PERMISSION;
- }
-
- return $statusValue;
- }
-
-}
diff --git a/includes/editpage/Constraint/UserRateLimitConstraint.php b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php
index 8f35dd5879d0..d25cf9336d88 100644
--- a/includes/editpage/Constraint/UserRateLimitConstraint.php
+++ b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php
@@ -25,31 +25,26 @@ use MediaWiki\Permissions\RateLimitSubject;
use StatusValue;
/**
- * Verify user doesn't exceed rate limits
+ * Verify that the user doesn't exceed 'linkpurge' limits, which are weird and special.
+ * Other rate limits have been integrated into their respective permission checks.
*
- * @since 1.36
+ * @since 1.44
* @internal
* @author DannyS712
*/
-class UserRateLimitConstraint implements IEditConstraint {
+class LinkPurgeRateLimitConstraint implements IEditConstraint {
private RateLimitSubject $subject;
- private string $oldContentModel;
- private string $newContentModel;
private RateLimiter $limiter;
private string $result;
public function __construct(
RateLimiter $limiter,
- RateLimitSubject $subject,
- string $oldContentModel,
- string $newContentModel
+ RateLimitSubject $subject
) {
$this->limiter = $limiter;
$this->subject = $subject;
- $this->oldContentModel = $oldContentModel;
- $this->newContentModel = $newContentModel;
}
private function limit( string $action, int $inc = 1 ): bool {
@@ -57,16 +52,10 @@ class UserRateLimitConstraint implements IEditConstraint {
}
public function checkConstraint(): string {
- // Need to check for rate limits on `editcontentmodel` if it is changing
- $contentModelChange = ( $this->newContentModel !== $this->oldContentModel );
-
// TODO inject and use a ThrottleStore once available, see T261744
// Checking if the user is rate limited increments the counts, so we cannot perform
// the check again when getting the status; thus, store the result
- if ( $this->limit( 'edit' )
- || $this->limit( 'linkpurge', 0 ) // only counted after the fact
- || ( $contentModelChange && $this->limit( 'editcontentmodel' ) )
- ) {
+ if ( $this->limit( 'linkpurge', /* only counted after the fact */ 0 ) ) {
$this->result = self::CONSTRAINT_FAILED;
} else {
$this->result = self::CONSTRAINT_PASSED;
diff --git a/includes/editpage/Constraint/UserBlockConstraint.php b/includes/editpage/Constraint/UserBlockConstraint.php
deleted file mode 100644
index bdd33262d434..000000000000
--- a/includes/editpage/Constraint/UserBlockConstraint.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-namespace MediaWiki\EditPage\Constraint;
-
-use MediaWiki\Linker\LinkTarget;
-use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\User\User;
-use StatusValue;
-
-/**
- * Verify user permissions:
- * Must not be blocked from the page
- *
- * @since 1.36
- * @internal
- * @author DannyS712
- */
-class UserBlockConstraint implements IEditConstraint {
-
- private PermissionManager $permissionManager;
- private LinkTarget $title;
- private User $user;
- private string $result;
-
- /**
- * @param PermissionManager $permissionManager
- * @param LinkTarget $title
- * @param User $user
- */
- public function __construct(
- PermissionManager $permissionManager,
- LinkTarget $title,
- User $user
- ) {
- $this->permissionManager = $permissionManager;
- $this->title = $title;
- $this->user = $user;
- }
-
- public function checkConstraint(): string {
- // Check isn't simple enough to just repeat when getting the status
- if ( $this->permissionManager->isBlockedFrom( $this->user, $this->title ) ) {
- $this->result = self::CONSTRAINT_FAILED;
- return self::CONSTRAINT_FAILED;
- }
-
- $this->result = self::CONSTRAINT_PASSED;
- return self::CONSTRAINT_PASSED;
- }
-
- public function getLegacyStatus(): StatusValue {
- $statusValue = StatusValue::newGood();
-
- if ( $this->result === self::CONSTRAINT_FAILED ) {
- $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
- }
-
- return $statusValue;
- }
-
-}
diff --git a/includes/editpage/EditPage.php b/includes/editpage/EditPage.php
index 93a3ad5a28e8..f08196894d88 100644
--- a/includes/editpage/EditPage.php
+++ b/includes/editpage/EditPage.php
@@ -37,6 +37,7 @@ use MediaWiki\Context\IContextSource;
use MediaWiki\Debug\DeprecationHelper;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\EditPage\Constraint\AccidentalRecreationConstraint;
+use MediaWiki\EditPage\Constraint\AuthorizationConstraint;
use MediaWiki\EditPage\Constraint\BrokenRedirectConstraint;
use MediaWiki\EditPage\Constraint\ChangeTagsConstraint;
use MediaWiki\EditPage\Constraint\ContentModelChangeConstraint;
@@ -54,7 +55,6 @@ use MediaWiki\EditPage\Constraint\PageSizeConstraint;
use MediaWiki\EditPage\Constraint\SelfRedirectConstraint;
use MediaWiki\EditPage\Constraint\SpamRegexConstraint;
use MediaWiki\EditPage\Constraint\UnicodeConstraint;
-use MediaWiki\EditPage\Constraint\UserBlockConstraint;
use MediaWiki\Exception\ErrorPageError;
use MediaWiki\Exception\MWContentSerializationException;
use MediaWiki\Exception\MWException;
@@ -2189,24 +2189,31 @@ class EditPage implements IEditObject {
)
);
$constraintRunner->addConstraint(
- $constraintFactory->newUserBlockConstraint( $this->mTitle, $requestUser )
+ $constraintFactory->newReadOnlyConstraint()
);
+
+ // Load the page data from the primary DB. If anything changes in the meantime,
+ // we detect it by using page_latest like a token in a 1 try compare-and-swap.
+ $this->page->loadPageData( IDBAccessObject::READ_LATEST );
+ $new = !$this->page->exists();
+
$constraintRunner->addConstraint(
- new ContentModelChangeConstraint(
+ new AuthorizationConstraint(
$authority,
$this->mTitle,
- $this->contentModel
+ $new
)
);
-
$constraintRunner->addConstraint(
- $constraintFactory->newReadOnlyConstraint()
+ new ContentModelChangeConstraint(
+ $authority,
+ $this->mTitle,
+ $this->contentModel
+ )
);
$constraintRunner->addConstraint(
- $constraintFactory->newUserRateLimitConstraint(
- $requestUser->toRateLimitSubject(),
- $this->mTitle->getContentModel(),
- $this->contentModel
+ $constraintFactory->newLinkPurgeRateLimitConstraint(
+ $requestUser->toRateLimitSubject()
)
);
$constraintRunner->addConstraint(
@@ -2230,16 +2237,6 @@ class EditPage implements IEditObject {
)
);
- // Load the page data from the primary DB. If anything changes in the meantime,
- // we detect it by using page_latest like a token in a 1 try compare-and-swap.
- $this->page->loadPageData( IDBAccessObject::READ_LATEST );
- $new = !$this->page->exists();
-
- // We do this last, as some of the other constraints are more specific
- $constraintRunner->addConstraint(
- $constraintFactory->newEditRightConstraint( $this->getUserForPermissions(), $this->mTitle, $new )
- );
-
// Check the constraints
if ( !$constraintRunner->checkConstraints() ) {
$failed = $constraintRunner->getFailedConstraint();
@@ -2625,9 +2622,12 @@ class EditPage implements IEditObject {
* result from the backend.
*/
private function handleFailedConstraint( IEditConstraint $failed ): void {
- if ( $failed instanceof UserBlockConstraint ) {
+ if ( $failed instanceof AuthorizationConstraint ) {
// Auto-block user's IP if the account was "hard" blocked
- if ( !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
+ if (
+ !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly()
+ && $failed->getLegacyStatus()->value === self::AS_BLOCKED_PAGE_FOR_USER
+ ) {
$this->context->getUser()->spreadAnyEditBlock();
}
} elseif ( $failed instanceof DefaultTextConstraint ) {
diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php
index 6cb4d8018da0..d088b87fc001 100644
--- a/includes/filerepo/file/File.php
+++ b/includes/filerepo/file/File.php
@@ -2465,7 +2465,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string HTML
*/
public function getLongDesc() {
$handler = $this->getHandler();
@@ -2477,7 +2477,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string HTML
*/
public function getShortDesc() {
$handler = $this->getHandler();
@@ -2489,7 +2489,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string plain text
*/
public function getDimensionsString() {
$handler = $this->getHandler();
diff --git a/includes/installer/Task/SqliteCreateDatabaseTask.php b/includes/installer/Task/SqliteCreateDatabaseTask.php
index 204a572ed06a..4298db4547e2 100644
--- a/includes/installer/Task/SqliteCreateDatabaseTask.php
+++ b/includes/installer/Task/SqliteCreateDatabaseTask.php
@@ -130,7 +130,9 @@ EOT;
}
}
# Put a .htaccess file in case the user didn't take our advice
- file_put_contents( "$dir/.htaccess", "Require all denied\n" );
+ file_put_contents( "$dir/.htaccess",
+ "Require all denied\n" .
+ "Satisfy All\n" );
return Status::newGood();
}
diff --git a/includes/installer/i18n/nan-hant.json b/includes/installer/i18n/nan-hant.json
index 7ca51f8702fc..59cbdd5709e9 100644
--- a/includes/installer/i18n/nan-hant.json
+++ b/includes/installer/i18n/nan-hant.json
@@ -15,7 +15,7 @@
"config-no-session": "你連線的資料已經無去矣,看你的php.ini,並且確定<code>session.save_path</code>是正確的目錄。",
"config-your-language": "你的語言:",
"config-your-language-help": "選一个安裝過程時欲用的話語",
- "config-wiki-language": "Wiki話語",
+ "config-wiki-language": "wiki語言:",
"config-wiki-language-help": "選一个Wiki大部份用的話",
"config-back": "← 倒退",
"config-continue": "繼續 →",
@@ -42,5 +42,6 @@
"config-header-postgres": "PostgreSQL設定",
"config-header-sqlite": "SQLite設定",
"config-project-namespace": "專案名空間:",
- "config-ns-generic": "專案"
+ "config-ns-generic": "專案",
+ "mainpagedocfooter": "請查看[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 用者說明書]的資料通使用wiki 軟體\n\n== 入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 配置的設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki時常問答]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki的公布列單]"
}
diff --git a/includes/installer/i18n/nan-latn-pehoeji.json b/includes/installer/i18n/nan-latn-pehoeji.json
index e3437e4d4805..8430b8f9272a 100644
--- a/includes/installer/i18n/nan-latn-pehoeji.json
+++ b/includes/installer/i18n/nan-latn-pehoeji.json
@@ -22,13 +22,10 @@
"config-session-error": "連線開始了的錯誤:$1",
"config-session-expired": "你連線資料已經過時矣,連線的使用期限是設做$1。你會使改共php.ini的<code>session.gc_maxlifetime</code>改較長,並且重新安裝動作。",
"config-no-session": "你連線的資料已經無去矣,看你的php.ini,並且確定<code>session.save_path</code>是正確的目錄。",
- "config-your-language": "你的話語:",
- "config-your-language-help": "選一个安裝過程時欲用的話語",
- "config-wiki-language": "Wiki話語",
- "config-wiki-language-help": "選一个Wiki大部份用的話",
+ "config-wiki-language": "Wiki gí-giân:",
"config-back": "← 倒退",
"config-continue": "繼續 →",
- "config-page-language": "話語",
+ "config-page-language": "Gí-giân",
"config-page-welcome": "歡迎來MediaWiki!",
"config-page-dbconnect": "連接去資料庫",
"config-page-upgrade": "共這馬的安裝升級",
@@ -53,6 +50,5 @@
"config-no-db": "揣無適合的資料庫驅動程式!你需要安裝 PHP 資料庫驅動程式。\n這馬支援下跤類型的資料庫: $1 。\n\n若你是家己編譯 PHP,你需要重新設定並且開資料庫客戶端,譬如:用 <code>./configure --with-mysqli</code> 指令參數。\n如你是用 Debian 或 Ubuntu 的套件安裝,你著需要閣另外安裝,例:<code>php-mysql</code> 套件。",
"config-outdated-sqlite": "<strong>Kéng-kò:</strong> Lí í-keng an-chng SQLite $1, m̄-koh i--ê pán-pún pí thang-chng--ê pán-pún $2 khah kū. Só͘-í lí bô-hoat-tō͘ ēng SQLite.",
"config-no-fts3": "<strong>Kéng-kò: </strong> SQLite tī pian-e̍k--ê sî-chūn bô pau-koat [//sqlite.org/fts3.html FTS3 module], āu-tâi chhiau-chhoē kong-lêng tiō ē bô-hoat-tō͘ iōng.",
- "mainpagetext": "'''MediaWiki已經裝好矣。'''",
- "mainpagedocfooter": "請查看[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 用者說明書]的資料通使用wiki 軟體\n\n== 入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 配置的設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki時常問答]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki的公布列單]"
+ "mainpagetext": "'''MediaWiki已經裝好矣。'''"
}
diff --git a/includes/installer/i18n/zh-hant.json b/includes/installer/i18n/zh-hant.json
index c081e46efc09..0321e4873481 100644
--- a/includes/installer/i18n/zh-hant.json
+++ b/includes/installer/i18n/zh-hant.json
@@ -169,6 +169,8 @@
"config-mysql-engine": "儲存引擎:",
"config-mysql-innodb": "InnoDB(推薦)",
"config-mysql-engine-help": "由於對同時連線有較好的處理能力,<strong>InnoDB</strong> 通常是最佳的選項。\n\n<strong>MyISAM</strong> 只在單人使用或者唯讀作業的情況之下才可能有較快的處理能力。\n相較於 InnoDB,MyISAM 也較容易出現資料損毀的情況。",
+ "config-server": "URL 主機名稱:",
+ "config-server-help": "用於存取您的 wiki 與 URL 的協定和主機名稱部分。自動偵測到的預設值在一般情況下會是正確的。",
"config-site-name": "wiki 的名稱:",
"config-site-name-help": "您所填入的內容會出現在瀏覽器的標題列以及各種其他地方。",
"config-site-name-blank": "請輸入網站名稱。",
@@ -302,6 +304,7 @@
"config-install-updates": "略過執行不需要的更新",
"config-install-updates-failed": "<strong>錯誤:</strong> 插入更新鍵值至資料表失敗,並出現以下錯誤:$1",
"config-install-sysop": "正在建立管理員使用者帳號",
+ "config-install-subscribe": "訂閱 mediawiki-announce",
"config-install-subscribe-fail": "無法訂閱 [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ mediawiki-announce]:$1",
"config-install-subscribe-notpossible": "未安裝 cURL,因此無法使用 <code>allow_url_fopen</code> 設定項目。",
"config-install-subscribe-alreadysubscribed": "您已訂閱 mediawiki-announce。[https://lists.wikimedia.org/postorius/accounts/per-subscription-preferences/ 檢視或變更您的訂閱設定]。",
@@ -315,6 +318,9 @@
"config-install-done-path": "<strong>恭喜!</strong>\n您已經成功安裝MediaWiki。\n\n安裝程式已自動產生 <code>LocalSettings.php</code> 檔案,\n該檔案中包含了您所有的設定項目。\n\n您需要下載該檔案,並將其放置在 <code>$4</code> 中,下載應已自動開始。\n\n若瀏覽器沒有提示您下載,或者您取消了下載,您可以點選下方連結重新下載:\n\n$3\n\n<strong>注意:</strong>如果您現在不下載此檔案,稍後結束安裝程式之後將無法再下載設定檔。\n\n當您完成本步驟後,您可以<strong>[$2 進入您的 wiki]</strong>。",
"config-install-success": "MediaWiki 已安裝成功。您現在可以在 <$1$2> 上檢視您的 wiki。若您有任何問題,請閱讀常見問題清單:<https://www.mediawiki.org/wiki/Manual:FAQ/zh>,或是利用在頁面上所連結的支援論壇之一。",
"config-install-db-success": "資料庫設定成功",
+ "config-install-generic": "正在執行任務「$1」",
+ "config-install-external-domains": "正在建立外部資料庫",
+ "config-skip-shared-domain": "略過可能的共享網域「$1」",
"config-download-localsettings": "下載 <code>LocalSettings.php</code>",
"config-help": "說明",
"config-help-tooltip": "點選以展開",
diff --git a/includes/language/LanguageEventIngress.php b/includes/language/LanguageEventIngress.php
index 18e083a47f39..d5960e04968e 100644
--- a/includes/language/LanguageEventIngress.php
+++ b/includes/language/LanguageEventIngress.php
@@ -2,7 +2,7 @@
namespace MediaWiki\Languages;
-use MediaWiki\DomainEvent\EventIngressBase;
+use MediaWiki\DomainEvent\DomainEventIngress;
use MediaWiki\Page\Event\PageDeletedEvent;
use MediaWiki\Page\Event\PageDeletedListener;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
@@ -17,7 +17,7 @@ use MessageCache;
* @internal
*/
class LanguageEventIngress
- extends EventIngressBase
+ extends DomainEventIngress
implements PageDeletedListener, PageRevisionUpdatedListener
{
diff --git a/includes/libs/filebackend/FSFileBackend.php b/includes/libs/filebackend/FSFileBackend.php
index 1d76f2bf9f5e..92ae176ee7e4 100644
--- a/includes/libs/filebackend/FSFileBackend.php
+++ b/includes/libs/filebackend/FSFileBackend.php
@@ -987,7 +987,8 @@ class FSFileBackend extends FileBackendStore {
* @return string
*/
protected function htaccessPrivate() {
- return "Require all denied\n";
+ return "Require all denied\n" .
+ "Satisfy All\n";
}
/**
diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php
index 5edb35bbdd9f..5e73704c6088 100644
--- a/includes/libs/objectcache/BagOStuff.php
+++ b/includes/libs/objectcache/BagOStuff.php
@@ -557,17 +557,6 @@ abstract class BagOStuff implements
}
/**
- * Clear the "last error" registry
- *
- * @since 1.23
- * @deprecated Since 1.38, hard deprecated in 1.43
- */
- public function clearLastError() {
- wfDeprecated( __METHOD__, '1.38' );
- $this->lastError = self::ERR_NONE;
- }
-
- /**
* Set the "last error" registry due to a problem encountered during an attempted operation
*
* @param int $error BagOStuff:ERR_* constant
diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php
index 925a2709158d..d2661d68bd7a 100644
--- a/includes/libs/objectcache/WANObjectCache.php
+++ b/includes/libs/objectcache/WANObjectCache.php
@@ -194,7 +194,7 @@ class WANObjectCache implements
protected $coalesceScheme;
/** @var TracerInterface */
- private $tracer = null;
+ private $tracer;
/** @var array<int,array> List of (key, UNIX timestamp) tuples for get() cache misses */
private $missLog;
@@ -461,6 +461,7 @@ class WANObjectCache implements
// Also, if no $info parameter is provided, then it doesn't matter how it changes here.
$legacyInfo = ( $info !== self::PASS_BY_REF );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key, $checkKeys );
$now = $this->getCurrentTime();
@@ -521,6 +522,7 @@ class WANObjectCache implements
// Also, if no $info parameter is provided, then it doesn't matter how it changes here.
$legacyInfo = ( $info !== self::PASS_BY_REF );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $keys, $checkKeys );
$curTTLs = [];
@@ -803,6 +805,7 @@ class WANObjectCache implements
* @return bool Success
*/
final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$keygroup = $this->determineKeyGroupForStats( $key );
@@ -1055,6 +1058,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
// Purge values must be stored under the value key so that WANObjectCache::set()
@@ -1116,7 +1120,9 @@ class WANObjectCache implements
* @return float UNIX timestamp
*/
final public function getCheckKeyTime( $key ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
+
return $this->getMultiCheckKeyTime( [ $key ] )[$key];
}
@@ -1182,6 +1188,7 @@ class WANObjectCache implements
* @since 1.31
*/
final public function getMultiCheckKeyTime( array $keys ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $keys );
$checkSisterKeysByKey = [];
@@ -1246,6 +1253,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP );
@@ -1298,6 +1306,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
public function resetCheckKey( $key ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP );
@@ -1618,7 +1627,9 @@ class WANObjectCache implements
final public function getWithSetCallback(
$key, $ttl, $callback, array $opts = [], array $cbParams = []
) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
+
$version = $opts['version'] ?? null;
$pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE;
$pCache = ( $pcTTL >= 0 )
@@ -1828,6 +1839,7 @@ class WANObjectCache implements
$preCallbackTime = $this->getCurrentTime();
++$this->callbackDepth;
// https://github.com/phan/phan/issues/4419
+ /** @noinspection PhpUnusedLocalVariableInspection */
$value = null;
try {
$value = $callback(
diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php
index c5ec3c403c8b..cc7a092e3341 100644
--- a/includes/media/MediaHandler.php
+++ b/includes/media/MediaHandler.php
@@ -818,7 +818,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public function getShortDesc( $file ) {
return self::getGeneralShortDesc( $file );
@@ -830,7 +830,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public function getLongDesc( $file ) {
return self::getGeneralLongDesc( $file );
@@ -840,7 +840,7 @@ abstract class MediaHandler {
* Used instead of getShortDesc if there is no handler registered for file.
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public static function getGeneralShortDesc( $file ) {
global $wgLang;
@@ -852,7 +852,7 @@ abstract class MediaHandler {
* Used instead of getLongDesc if there is no handler registered for file.
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public static function getGeneralLongDesc( $file ) {
return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
@@ -882,7 +882,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string Dimensions
+ * @return string Dimensions (plain text)
*/
public function getDimensionsString( $file ) {
return '';
diff --git a/includes/page/Article.php b/includes/page/Article.php
index c6ff94f24503..962990012952 100644
--- a/includes/page/Article.php
+++ b/includes/page/Article.php
@@ -784,7 +784,7 @@ class Article implements Page {
$outputPage->setRevisionId( $this->getRevIdFetched() );
$outputPage->setRevisionIsCurrent( $rev->isCurrent() );
# Preload timestamp to avoid a DB hit
- $outputPage->setRevisionTimestamp( $rev->getTimestamp() );
+ $outputPage->getMetadata()->setRevisionTimestamp( $rev->getTimestamp() );
# Pages containing custom CSS or JavaScript get special treatment
if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) {
@@ -929,7 +929,7 @@ class Article implements Page {
# Preload timestamp to avoid a DB hit
$cachedTimestamp = $pOutput->getRevisionTimestamp();
if ( $cachedTimestamp !== null ) {
- $outputPage->setRevisionTimestamp( $cachedTimestamp );
+ $outputPage->getMetadata()->setRevisionTimestamp( $cachedTimestamp );
$this->mPage->setTimestamp( $cachedTimestamp );
}
}
@@ -972,7 +972,7 @@ class Article implements Page {
$cachedId = $pOutput->getCacheRevisionId();
if ( $cachedId !== null ) {
$outputPage->setRevisionId( $cachedId );
- $outputPage->setRevisionTimestamp( $pOutput->getTimestamp() );
+ $outputPage->getMetadata()->setRevisionTimestamp( $pOutput->getTimestamp() );
}
}
diff --git a/includes/page/Event/PageDeletedListener.php b/includes/page/Event/PageDeletedListener.php
index 93ad81570fe1..9b3eaa2af8b5 100644
--- a/includes/page/Event/PageDeletedListener.php
+++ b/includes/page/Event/PageDeletedListener.php
@@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event;
* event type 'PageDeleted', see PageDeletedEvent::TYPE.
*
* @see PageDeletedEvent
+ * @unstable until 1.45, should become stable to implement
*/
interface PageDeletedListener {
diff --git a/includes/page/Event/PageMovedListener.php b/includes/page/Event/PageMovedListener.php
index 041812c26574..28dc060c9b2c 100644
--- a/includes/page/Event/PageMovedListener.php
+++ b/includes/page/Event/PageMovedListener.php
@@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event;
* event type 'PageMoved', see PageMovedEvent::TYPE.
*
* @see PageMovedEvent
+ * @unstable until 1.45, should become stable to implement
*/
interface PageMovedListener {
diff --git a/includes/page/Event/PageRevisionUpdatedEvent.php b/includes/page/Event/PageRevisionUpdatedEvent.php
index c65487d01570..7d8cab6ec4ff 100644
--- a/includes/page/Event/PageRevisionUpdatedEvent.php
+++ b/includes/page/Event/PageRevisionUpdatedEvent.php
@@ -48,11 +48,11 @@ use Wikimedia\Assert\Assert;
*
* Extensions that want to subscribe to this event should list
* "PageRevisionUpdated" as a subscribed event type.
- * Subscribers based on EventIngressBase should implement the
+ * Subscribers based on DomainEventIngress should implement the
* handlePageRevisionUpdatedEvent() listener method to be informed when
* a page update has been committed to the database.
*
- * See the documentation of EventIngressBase and DomainEventSource for
+ * See the documentation of DomainEventIngress and DomainEventSource for
* more options and details.
*
* @unstable until 1.45
diff --git a/includes/page/Event/PageRevisionUpdatedListener.php b/includes/page/Event/PageRevisionUpdatedListener.php
index 598da5cb848d..d2ed812d712a 100644
--- a/includes/page/Event/PageRevisionUpdatedListener.php
+++ b/includes/page/Event/PageRevisionUpdatedListener.php
@@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event;
* event type 'PageRevisionUpdated', see PageRevisionUpdatedEvent::TYPE.
*
* @see PageRevisionUpdatedEvent
+ * @unstable until 1.45, should become stable to implement
*/
interface PageRevisionUpdatedListener {
diff --git a/includes/page/Event/PageStateListener.php b/includes/page/Event/PageStateListener.php
index 3ff3c1427ef2..6629bae38ba0 100644
--- a/includes/page/Event/PageStateListener.php
+++ b/includes/page/Event/PageStateListener.php
@@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event;
* event type 'PageState', see PageStateEvent::TYPE.
*
* @see PageStateEvent
+ * @unstable until 1.45, should become stable to implement
*/
interface PageStateListener {
diff --git a/includes/pager/ContributionsPager.php b/includes/pager/ContributionsPager.php
index ebd1efa87869..fdebeb41f92e 100644
--- a/includes/pager/ContributionsPager.php
+++ b/includes/pager/ContributionsPager.php
@@ -920,12 +920,14 @@ abstract class ContributionsPager extends RangeChronologicalPager {
$revUserId = $revUser ? $revUser->getId() : 0;
$revUserText = $revUser ? $revUser->getName() : '';
if ( $this->target !== $revUserText ) {
- $userlink = ' <span class="mw-changeslist-separator"></span> '
- . Html::rawElement( 'bdi', [ 'dir' => $dir ],
- Linker::userLink( $revUserId, $revUserText ) );
- $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
- Linker::userTalkLink( $revUserId, $revUserText ) )->escaped() . ' ';
+ $userPageLink = Linker::userLink( $revUserId, $revUserText );
+ $userTalkLink = Linker::userTalkLink( $revUserId, $revUserText );
+
+ $userlink = ' <span class="mw-changeslist-separator"></span> ' .
+ Html::rawElement( 'bdi', [ 'dir' => $dir ], $userPageLink ) .
+ Linker::renderUserToolLinksArray( [ $userTalkLink ], false );
}
+
return $userlink;
}
diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php
index e364bf0ae2b0..971442f2c486 100644
--- a/includes/parser/ParserOptions.php
+++ b/includes/parser/ParserOptions.php
@@ -1099,6 +1099,7 @@ class ParserOptions {
* Get a ParserOptions object from a given user.
* Language will be taken from $wgLang.
*
+ * @since 1.13
* @param UserIdentity $user
* @return ParserOptions
*/
@@ -1109,6 +1110,7 @@ class ParserOptions {
/**
* Get a ParserOptions object from a given user and language
*
+ * @since 1.19
* @param UserIdentity $user
* @param Language $lang
* @return ParserOptions
@@ -1120,6 +1122,7 @@ class ParserOptions {
/**
* Get a ParserOptions object from a IContextSource object
*
+ * @since 1.19
* @param IContextSource $context
* @return ParserOptions
*/
diff --git a/includes/parser/Parsoid/Config/SiteConfig.php b/includes/parser/Parsoid/Config/SiteConfig.php
index 4e91f19aa96d..8a61b61cde11 100644
--- a/includes/parser/Parsoid/Config/SiteConfig.php
+++ b/includes/parser/Parsoid/Config/SiteConfig.php
@@ -822,6 +822,14 @@ class SiteConfig extends ISiteConfig {
return $this->config->get( MainConfigNames::ExternalLinkTarget );
}
+ /**
+ * Return the localization key we should use for asynchronous
+ * fallback content.
+ */
+ public function getAsyncFallbackMessageKey(): string {
+ return 'parsoid-async-not-ready-fallback';
+ }
+
// MW-specific helper
/**
diff --git a/includes/recentchanges/ChangeTrackingEventIngress.php b/includes/recentchanges/ChangeTrackingEventIngress.php
index f4efe4136820..3da2b54f4b35 100644
--- a/includes/recentchanges/ChangeTrackingEventIngress.php
+++ b/includes/recentchanges/ChangeTrackingEventIngress.php
@@ -5,10 +5,12 @@ namespace MediaWiki\RecentChanges;
use LogicException;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Config\Config;
-use MediaWiki\DomainEvent\EventIngressBase;
+use MediaWiki\Content\IContentHandlerFactory;
+use MediaWiki\DomainEvent\DomainEventIngress;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\JobQueue\JobQueueGroup;
+use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob;
use MediaWiki\JobQueue\Jobs\RevertedTagUpdateJob;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
@@ -30,7 +32,7 @@ use MediaWiki\User\UserNameUtils;
* @internal
*/
class ChangeTrackingEventIngress
- extends EventIngressBase
+ extends DomainEventIngress
implements PageRevisionUpdatedListener
{
@@ -60,7 +62,8 @@ class ChangeTrackingEventIngress
'UserNameUtils',
'TalkPageNotificationManager',
'MainConfig',
- 'JobQueueGroup'
+ 'JobQueueGroup',
+ 'ContentHandlerFactory',
],
'events' => [ // see registerListeners()
PageRevisionUpdatedEvent::TYPE
@@ -75,7 +78,9 @@ class ChangeTrackingEventIngress
private UserNameUtils $userNameUtils;
private TalkPageNotificationManager $talkPageNotificationManager;
private JobQueueGroup $jobQueueGroup;
+ private IContentHandlerFactory $contentHandlerFactory;
private bool $useRcPatrol;
+ private bool $rcWatchCategoryMembership;
public function __construct(
ChangeTagsStore $changeTagsStore,
@@ -86,7 +91,8 @@ class ChangeTrackingEventIngress
UserNameUtils $userNameUtils,
TalkPageNotificationManager $talkPageNotificationManager,
Config $mainConfig,
- JobQueueGroup $jobQueueGroup
+ JobQueueGroup $jobQueueGroup,
+ IContentHandlerFactory $contentHandlerFactory
) {
// NOTE: keep in sync with self::OBJECT_SPEC
$this->changeTagsStore = $changeTagsStore;
@@ -97,8 +103,12 @@ class ChangeTrackingEventIngress
$this->userNameUtils = $userNameUtils;
$this->talkPageNotificationManager = $talkPageNotificationManager;
$this->jobQueueGroup = $jobQueueGroup;
+ $this->contentHandlerFactory = $contentHandlerFactory;
$this->useRcPatrol = $mainConfig->get( MainConfigNames::UseRCPatrol );
+ $this->rcWatchCategoryMembership = $mainConfig->get(
+ MainConfigNames::RCWatchCategoryMembership
+ );
}
public static function newForTesting(
@@ -110,7 +120,8 @@ class ChangeTrackingEventIngress
UserNameUtils $userNameUtils,
TalkPageNotificationManager $talkPageNotificationManager,
Config $mainConfig,
- JobQueueGroup $jobQueueGroup
+ JobQueueGroup $jobQueueGroup,
+ IContentHandlerFactory $contentHandlerFactory
) {
$ingress = new self(
$changeTagsStore,
@@ -121,7 +132,8 @@ class ChangeTrackingEventIngress
$userNameUtils,
$talkPageNotificationManager,
$mainConfig,
- $jobQueueGroup
+ $jobQueueGroup,
+ $contentHandlerFactory
);
$ingress->initSubscriber( self::OBJECT_SPEC );
return $ingress;
@@ -163,12 +175,16 @@ class ChangeTrackingEventIngress
);
}
- if ( $event->isEffectiveContentChange() && !$event->isImplicit() ) {
- $this->updateUserEditTrackerAfterPageUpdated(
- $event->getPerformer()
- );
+ if ( $event->isEffectiveContentChange() ) {
+ $this->generateCategoryMembershipChanges( $event );
+
+ if ( !$event->isImplicit() ) {
+ $this->updateUserEditTrackerAfterPageUpdated(
+ $event->getPerformer()
+ );
- $this->updateNewTalkAfterPageUpdated( $event );
+ $this->updateNewTalkAfterPageUpdated( $event );
+ }
}
if ( $event->isRevert() && $event->isEffectiveContentChange() ) {
@@ -176,6 +192,49 @@ class ChangeTrackingEventIngress
}
}
+ /**
+ * Create RC entries for category changes that resulted from this update
+ * if the relevant config is enabled.
+ * This should only be triggered for actual edits, not reconciliation events (T390636).
+ *
+ * @param PageRevisionUpdatedEvent $event
+ */
+ private function generateCategoryMembershipChanges( PageRevisionUpdatedEvent $event ): void {
+ if ( $this->rcWatchCategoryMembership
+ && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE )
+ && $this->anyChangedSlotSupportsCategories( $event )
+ ) {
+ // Note: jobs are pushed after deferred updates, so the job should be able to see
+ // the recent change entry (also done via deferred updates) and carry over any
+ // bot/deletion/IP flags, ect.
+ $this->jobQueueGroup->lazyPush(
+ CategoryMembershipChangeJob::newSpec(
+ $event->getPage(),
+ $event->getLatestRevisionAfter()->getTimestamp(),
+ $event->hasCause( PageRevisionUpdatedEvent::CAUSE_IMPORT )
+ )
+ );
+ }
+ }
+
+ /**
+ * Determine whether any slots changed in this update supports categories.
+ * @param PageRevisionUpdatedEvent $event
+ * @return bool
+ */
+ private function anyChangedSlotSupportsCategories( PageRevisionUpdatedEvent $event ): bool {
+ $slotsUpdate = $event->getSlotsUpdate();
+ foreach ( $slotsUpdate->getModifiedRoles() as $role ) {
+ $model = $slotsUpdate->getModifiedSlot( $role )->getModel();
+
+ if ( $this->contentHandlerFactory->getContentHandler( $model )->supportsCategories() ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private function updateChangeTagsAfterPageUpdated( array $tags, int $revId ) {
$this->changeTagsStore->addTags( $tags, null, $revId );
}
diff --git a/includes/search/SearchEventIngress.php b/includes/search/SearchEventIngress.php
index 5ee81f4150eb..f6aff749f797 100644
--- a/includes/search/SearchEventIngress.php
+++ b/includes/search/SearchEventIngress.php
@@ -2,7 +2,7 @@
namespace MediaWiki\Search;
-use MediaWiki\DomainEvent\EventIngressBase;
+use MediaWiki\DomainEvent\DomainEventIngress;
use MediaWiki\Page\Event\PageDeletedEvent;
use MediaWiki\Page\Event\PageDeletedListener;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
@@ -17,7 +17,7 @@ use MediaWiki\Revision\SlotRecord;
* @internal
*/
class SearchEventIngress
- extends EventIngressBase
+ extends DomainEventIngress
implements PageDeletedListener, PageRevisionUpdatedListener
{
diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php
index 8584e96567dc..e1c9388f1884 100644
--- a/includes/specials/SpecialVersion.php
+++ b/includes/specials/SpecialVersion.php
@@ -225,6 +225,7 @@ class SpecialVersion extends SpecialPage {
$this->getLibraries( $credits ),
$this->getParserTags(),
$this->getParserFunctionHooks(),
+ $this->getParsoidModules(),
$this->getHooks(),
$this->IPInfo(),
];
@@ -954,6 +955,45 @@ class SpecialVersion extends SpecialPage {
}
/**
+ * Obtains a list of installed Parsoid Modules and the associated H2 header
+ *
+ * @return string HTML output
+ */
+ protected function getParsoidModules() {
+ $siteConfig = MediaWikiServices::getInstance()->getParsoidSiteConfig();
+ $modules = $siteConfig->getExtensionModules();
+
+ if ( !$modules ) {
+ return '';
+ }
+
+ $this->addTocSection( 'version-parsoid-modules', 'mw-version-parsoid-modules' );
+
+ $out = Html::rawElement(
+ 'h2',
+ [ 'id' => 'mw-version-parsoid-modules' ],
+ Html::rawElement(
+ 'span',
+ [ 'class' => 'plainlinks' ],
+ $this->getLinkRenderer()->makeExternalLink(
+ 'https://www.mediawiki.org/wiki/Special:MyLanguage/Parsoid',
+ $this->msg( 'version-parsoid-modules' ),
+ $this->getFullTitle()
+ )
+ )
+ );
+
+ $moduleNames = array_map(
+ static fn ( $m )=>Html::element( 'code', [], $m->getConfig()['name'] ),
+ $modules
+ );
+
+ $out .= $this->getLanguage()->listToText( $moduleNames );
+
+ return $out;
+ }
+
+ /**
* Creates and returns the HTML for a single extension category.
*
* @since 1.17