"module": false,
"mw": false,
"$": false,
- "mediaWiki": false,
- "jQuery": false,
"OO": false
},
"rules": {
'help', 'help-message', 'help-messages' instead.
* (T197179) HTMLFormField::getNotices() is now deprecated.
* The jquery.localize module is now deprecated. Use jquery.i18n instead.
+* The SecondaryDataUpdates hook was deprecated in favor of RevisionDataUpdates,
+ or overriding ContentHandler::getSecondaryDataUpdates (T194038).
+* The WikiPageDeletionUpdates hook was deprecated in favor of
+ PageDeletionDataUpdates, or overriding ContentHandler::getDeletionDataUpdates
+ (T194038).
+* Content::getSecondaryDataUpdates has been deprecated in favor of
+ ContentHandler::getSecondaryDataUpdates() for overriding by extensions
+ (T194038).
+ Application logic should call WikiPage::doSecondaryDataUpdates() (T194037).
+* Content::getDeletionUpdates has been deprecated in favor of
+ ContentHandler::getDeletionUpdates() for overriding by extensions (T194038).
+ Application logic should call WikiPage::doSecondaryDataUpdates() (T194037).
=== Other changes in 1.32 ===
* (T198811) The following tables have had their UNIQUE indexes turned into
'MediaWiki\\ProcOpenError' => __DIR__ . '/includes/exception/ProcOpenError.php',
'MediaWiki\\Revision\\RenderedRevision' => __DIR__ . '/includes/Revision/RenderedRevision.php',
'MediaWiki\\Revision\\RevisionRenderer' => __DIR__ . '/includes/Revision/RevisionRenderer.php',
+ 'MediaWiki\\Revision\\SlotRenderingProvider' => __DIR__ . '/includes/Revision/SlotRenderingProvider.php',
'MediaWiki\\Search\\ParserOutputSearchDataExtractor' => __DIR__ . '/includes/search/ParserOutputSearchDataExtractor.php',
'MediaWiki\\ShellDisabledError' => __DIR__ . '/includes/exception/ShellDisabledError.php',
'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
(Used to be called $baseRevId.)
$undidRevId: the rev ID (or 0) this edit undid
+'PageDeletionDataUpdates': Called when constructing a list of DeferrableUpdate to be
+executed when a page is deleted.
+$title The Title of the page being deleted.
+$revision A RevisionRecord representing the page's current revision at the time of deletion.
+&$updates A list of DeferrableUpdate that can be manipulated by the hook handler.
+
'PageHistoryBeforeList': When a history page list is about to be constructed.
&$article: the article that the history is loading for
$context: RequestContext object
added to any module.
&$ResourceLoader: object
+'RevisionDataUpdates': Called when constructing a list of DeferrableUpdate to be
+executed to record secondary data about a revision.
+$title The Title of the page the revision belongs to
+$renderedRevision a RenderedRevision object representing the new revision and providing access
+ to the RevisionRecord as well as ParserOutput of that revision.
+&$updates A list of DeferrableUpdate that can be manipulated by the hook handler.
+
'RevisionRecordInserted': Called after a revision is inserted into the database.
$revisionRecord: the RevisionRecord that has just been inserted.
Note that lists should be in the format name => object and the names in both
lists should be distinct.
-'SecondaryDataUpdates': Allows modification of the list of DataUpdates to
-perform when page content is modified. Currently called by
-AbstractContent::getSecondaryDataUpdates.
+'SecondaryDataUpdates': DEPRECATED! Use RevisionDataUpdates or override
+ContentHandler::getSecondaryDataUpdates instead.
+Allows modification of the list of DataUpdates to perform when page content is modified.
$title: Title of the page that is being edited.
$oldContent: Content object representing the page's content before the edit.
$recursive: bool indicating whether DataUpdates should trigger recursive
&$opts: Options to use for the query
&$join: Join conditions
-'WikiPageDeletionUpdates': manipulate the list of DeferrableUpdates to be
-applied when a page is deleted. Called in WikiPage::getDeletionUpdates(). Note
-that updates specific to a content model should be provided by the respective
-Content's getDeletionUpdates() method.
+'WikiPageDeletionUpdates': DEPRECATED! Use PageDeletionDataUpdates or
+override ContentHandler::getDeletionDataUpdates instead.
+Manipulates the list of DeferrableUpdates to be applied when a page is deleted.
$page: the WikiPage
$content: the Content to generate updates for, or null in case the page revision
could not be loaded. The delete will succeed despite this.
*
* @since 1.32
*/
-class RenderedRevision {
+class RenderedRevision implements SlotRenderingProvider {
/**
* @var Title
--- /dev/null
+<?php
+/**
+ * Created by PhpStorm.
+ * User: daki
+ * Date: 05.09.18
+ * Time: 16:08
+ */
+namespace MediaWiki\Revision;
+
+use MediaWiki\Storage\SuppressedDataException;
+use ParserOutput;
+
+/**
+ * A lazy provider of ParserOutput objects for a revision's individual slots.
+ *
+ * @since 1.32
+ */
+interface SlotRenderingProvider {
+
+ /**
+ * @param string $role
+ * @param array $hints Hints given as an associative array. Known keys:
+ * - 'generate-html' => bool: Whether the caller is interested in output HTML (as opposed
+ * to just meta-data). Default is to generate HTML.
+ *
+ * @throws SuppressedDataException if the content is not accessible for the audience
+ * specified in the constructor.
+ * @return ParserOutput
+ */
+ public function getSlotParserOutput( $role, array $hints = [] );
+
+}
use Content;
use ContentHandler;
use DataUpdate;
+use DeferrableUpdate;
use DeferredUpdates;
use Hooks;
use IDBAccessObject;
use InvalidArgumentException;
use JobQueueGroup;
use Language;
+use LinksDeletionUpdate;
use LinksUpdate;
use LogicException;
use MediaWiki\Edit\PreparedEdit;
*
* Contains the following fields:
* - oldRevision (RevisionRecord|null): the revision that was current before the change
- * associated with this update. Might not be set, use getOldRevision() instead of direct
- * access.
+ * associated with this update. Might not be set, use getParentRevision().
* - oldId (int|null): the id of the above revision. 0 if there is no such revision (the change
* was about creating a new page); null if not known (that should not happen).
* - oldIsRedirect (bool|null): whether the page was a redirect before the change. Lazy-loaded,
*/
private $slotsUpdate = null;
+ /**
+ * @var RevisionRecord|null
+ */
+ private $parentRevision = null;
+
/**
* @var RevisionRecord|null
*/
}
/**
- * Returns the revision that was current before the edit. This would be null if the edit
- * created the page, or the revision's parent for a regular edit, or the revision itself
- * for a null-edit.
- * Only defined after calling grabCurrentRevision() or prepareContent() or prepareUpdate()!
+ * Returns the parent revision of the new revision wrapped by this update.
+ * If the update is a null-edit, this will return the parent of the current (and new) revision.
+ * This will return null if the revision wrapped by this update created the page.
+ * Only defined after calling prepareContent() or prepareUpdate()!
*
- * @return RevisionRecord|null the revision that was current before the edit, or null if
- * the edit created the page.
+ * @return RevisionRecord|null the parent revision of the new revision, or null if
+ * the update created the page.
*/
- private function getOldRevision() {
- $this->assertHasPageState( __METHOD__ );
+ private function getParentRevision() {
+ $this->assertPrepared( __METHOD__ );
- // If 'oldRevision' is not set, load it!
- // Useful if $this->oldPageState is initialized by prepareUpdate.
- if ( !array_key_exists( 'oldRevision', $this->pageState ) ) {
- /** @var int $oldId */
- $oldId = $this->pageState['oldId'];
- $flags = $this->useMaster() ? RevisionStore::READ_LATEST : 0;
- $this->pageState['oldRevision'] = $oldId
- ? $this->revisionStore->getRevisionById( $oldId, $flags )
- : null;
+ if ( $this->parentRevision ) {
+ return $this->parentRevision;
}
- return $this->pageState['oldRevision'];
+ if ( !$this->pageState['oldId'] ) {
+ // If there was no current revision, there is no parent revision,
+ // since the page didn't exist.
+ return null;
+ }
+
+ $oldId = $this->revision->getParentId();
+ $flags = $this->useMaster() ? RevisionStore::READ_LATEST : 0;
+ $this->parentRevision = $oldId
+ ? $this->revisionStore->getRevisionById( $oldId, $flags )
+ : null;
+
+ return $this->parentRevision;
}
/**
* @note After prepareUpdate() was called, grabCurrentRevision() will throw an exception
* to avoid confusion, since the page's current revision is then the new revision after
* the edit, which was presumably passed to prepareUpdate() as the $revision parameter.
- * Use getOldRevision() instead to access the revision that used to be current before the
- * edit.
+ * Use getParentRevision() instead to access the revision that is the parent of the
+ * new revision.
*
* @return RevisionRecord|null the page's current revision, or null if the page does not
* yet exist.
// prepareUpdate() is redundant for null-edits
$this->doTransition( 'has-revision' );
+ } else {
+ $this->parentRevision = $parentRevision;
}
}
$this->assertPrepared( __METHOD__ );
if ( !$this->slotsUpdate ) {
- $old = $this->getOldRevision();
+ $old = $this->getParentRevision();
$this->slotsUpdate = RevisionSlotsUpdate::newFromRevisionSlots(
$this->revision->getSlots(),
$old ? $old->getSlots() : null
/**
* @param bool $recursive
*
- * @return DataUpdate[]
+ * @return DeferrableUpdate[]
*/
public function getSecondaryDataUpdates( $recursive = false ) {
- // TODO: MCR: getSecondaryDataUpdates() needs a complete overhaul to avoid DataUpdates
- // from different slots overwriting each other in the database. Plan:
- // * replace direct calls to Content::getSecondaryDataUpdates() with calls to this method
- // * Construct LinksUpdate here, on the combined ParserOutput, instead of in AbstractContent
- // for each slot.
- // * Pass $slot into getSecondaryDataUpdates() - probably be introducing a new duplicate
- // version of this function in ContentHandler.
- // * The new method gets the PreparedEdit, but no $recursive flag (that's for LinksUpdate)
- // * Hack: call both the old and the new getSecondaryDataUpdates method here; Pass
- // the per-slot ParserOutput to the old method, for B/C.
- // * Hack: If there is more than one slot, filter LinksUpdate from the DataUpdates
- // returned by getSecondaryDataUpdates, and use a LinksUpdated for the combined output
- // instead.
- // * Call the SecondaryDataUpdates hook here (or kill it - its signature doesn't make sense)
-
- $content = $this->getSlots()->getContent( 'main' );
-
- // NOTE: $output is the combined output, to be shown in the default view.
+ if ( $this->isContentDeleted() ) {
+ // This shouldn't happen, since the current content is always public,
+ // and DataUpates are only needed for current content.
+ return [];
+ }
+
$output = $this->getCanonicalParserOutput();
- $updates = $content->getSecondaryDataUpdates(
- $this->getTitle(), null, $recursive, $output
+ // Construct a LinksUpdate for the combined canonical output.
+ $linksUpdate = new LinksUpdate(
+ $this->getTitle(),
+ $output,
+ $recursive
);
- return $updates;
+ $allUpdates = [ $linksUpdate ];
+
+ // NOTE: Run updates for all slots, not just the modified slots! Otherwise,
+ // info for an inherited slot may end up being removed. This is also needed
+ // to ensure that purges are effective.
+ $renderedRevision = $this->getRenderedRevision();
+ foreach ( $this->getSlots()->getSlotRoles() as $role ) {
+ $slot = $this->getRawSlot( $role );
+ $content = $slot->getContent();
+ $handler = $content->getContentHandler();
+
+ $updates = $handler->getSecondaryDataUpdates(
+ $this->getTitle(),
+ $content,
+ $role,
+ $renderedRevision
+ );
+ $allUpdates = array_merge( $allUpdates, $updates );
+
+ // TODO: remove B/C hack in 1.32!
+ // NOTE: we assume that the combined output contains all relevant meta-data for
+ // all slots!
+ $legacyUpdates = $content->getSecondaryDataUpdates(
+ $this->getTitle(),
+ null,
+ $recursive,
+ $output
+ );
+
+ // HACK: filter out redundant and incomplete LinksUpdates
+ $legacyUpdates = array_filter( $legacyUpdates, function ( $update ) {
+ return !( $update instanceof LinksUpdate );
+ } );
+
+ $allUpdates = array_merge( $allUpdates, $legacyUpdates );
+ }
+
+ // XXX: if a slot was removed by an earlier edit, but deletion updates failed to run at
+ // that time, we don't know for which slots to run deletion updates when purging a page.
+ // We'd have to examine the entire history of the page to determine that. Perhaps there
+ // could be a "try extra hard" mode for that case that would run a DB query to find all
+ // roles/models ever used on the page. On the other hand, removing slots should be quite
+ // rare, so perhaps this isn't worth the trouble.
+
+ // TODO: consolidate with similar logic in WikiPage::getDeletionUpdates()
+ $wikiPage = $this->getWikiPage();
+ $parentRevision = $this->getParentRevision();
+ foreach ( $this->getRemovedSlotRoles() as $role ) {
+ // HACK: we should get the content model of the removed slot from a SlotRoleHandler!
+ // For now, find the slot in the parent revision - if the slot was removed, it should
+ // always exist in the parent revision.
+ $parentSlot = $parentRevision->getSlot( $role, RevisionRecord::RAW );
+ $content = $parentSlot->getContent();
+ $handler = $content->getContentHandler();
+
+ $updates = $handler->getDeletionUpdates(
+ $this->getTitle(),
+ $role
+ );
+ $allUpdates = array_merge( $allUpdates, $updates );
+
+ // TODO: remove B/C hack in 1.32!
+ $legacyUpdates = $content->getDeletionUpdates( $wikiPage );
+
+ // HACK: filter out redundant and incomplete LinksDeletionUpdate
+ $legacyUpdates = array_filter( $legacyUpdates, function ( $update ) {
+ return !( $update instanceof LinksDeletionUpdate );
+ } );
+
+ $allUpdates = array_merge( $allUpdates, $legacyUpdates );
+ }
+
+ // TODO: hard deprecate SecondaryDataUpdates in favor of RevisionDataUpdates in 1.33!
+ Hooks::run(
+ 'RevisionDataUpdates',
+ [ $this->getTitle(), $renderedRevision, &$allUpdates ]
+ );
+
+ return $allUpdates;
}
/**
WikiPage::onArticleEdit( $title, $legacyRevision, $this->getTouchedSlotRoles() );
}
- $oldRevision = $this->getOldRevision();
+ $oldRevision = $this->getParentRevision();
$oldLegacyRevision = $oldRevision ? new Revision( $oldRevision ) : null;
// TODO: In the wiring, register a listener for this on the new PageEventEmitter
}
foreach ( $updates as $update ) {
- $update->setCause( $causeAction, $causeAgent );
+ if ( $update instanceof DataUpdate ) {
+ $update->setCause( $causeAction, $causeAgent );
+ }
if ( $update instanceof LinksUpdate ) {
$update->setRevision( $legacyRevision );
$update->setTriggeringUser( $triggeringUser );
}
if ( $this->params['testactions'] ) {
- $limit = $this->getMain()->canApiHighLimits() ? self::LIMIT_SML1 : self::LIMIT_SML2;
+ $limit = $this->getMain()->canApiHighLimits() ? self::LIMIT_SML2 : self::LIMIT_SML1;
if ( $this->countTestedActions >= $limit ) {
return null; // force a continuation
}
"apihelp-options-example-complex": "重置所有偏好設定,然後再設定 <kbd>skin</kbd> 與 <kbd>nickname</kbd>。",
"apihelp-paraminfo-summary": "獲得有關 API 模組的資訊。",
"apihelp-paraminfo-param-helpformat": "說明字串的格式。",
+ "apihelp-paraminfo-param-formatmodules": "格式模組名稱清單(<var>format</var> 參數的值)。請改用 <var>$1modules</var> 。",
"apihelp-paraminfo-example-1": "顯示 <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>、<kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>、<kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>、和 <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> 的資訊。",
"apihelp-paraminfo-example-2": "顯示 <kbd>[[Special:ApiHelp/query|action=query]]</kbd> 所有子模組的資訊。",
"apihelp-parse-summary": "解析內容併回傳解析器輸出。",
"apihelp-query+allimages-param-to": "要停止列舉的圖片標題。僅能與 $1sort=name 一起使用。",
"apihelp-query+allimages-param-start": "要開始列舉的時間戳記。僅能與 $1sort=timestamp 一起使用。",
"apihelp-query+allimages-param-end": "要停止列舉的時間戳記。僅能與 $1sort=timestamp 一起使用。",
+ "apihelp-query+allimages-param-prefix": "搜尋所有以此值為開頭的圖片。僅能與 $1sort=name 一起使用。",
"apihelp-query+allimages-param-minsize": "限制圖片至少要有這樣多的位元組。",
"apihelp-query+allimages-param-maxsize": "限制圖片最多只能這樣多的位元組。",
"apihelp-query+allimages-param-sha1": "圖片的 SHA1 雜湊值。覆蓋 $1sha1base36。",
"apihelp-query+allusers-example-Y": "列出以<kbd>Y</kbd>開頭的使用者。",
"apihelp-query+authmanagerinfo-summary": "取得目前身分核對狀態的資訊。",
"apihelp-query+backlinks-summary": "找出連結至指定頁面的所有頁面。",
+ "apihelp-query+backlinks-param-title": "要搜尋的標題。不能與 <var>$1pageid</var> 一起使用。",
"apihelp-query+backlinks-param-pageid": "要搜尋的頁面 ID。不能與 <var>$1title</var> 一起使用。",
"apihelp-query+backlinks-param-namespace": "要列舉的命名空間。",
"apihelp-query+backlinks-param-dir": "列出時所採用的方向。",
"apihelp-query+blocks-paramvalue-prop-userid": "添加已封鎖使用者的使用者 ID。",
"apihelp-query+blocks-paramvalue-prop-by": "添加進行封鎖中的使用者之使用者名稱。",
"apihelp-query+blocks-paramvalue-prop-byid": "添加進行封鎖中的使用者之使用者 ID。",
+ "apihelp-query+blocks-paramvalue-prop-timestamp": "添加當封鎖生效的時間戳記。",
+ "apihelp-query+blocks-paramvalue-prop-expiry": "添加當封鎖到期的時間戳記。",
"apihelp-query+blocks-paramvalue-prop-reason": "添加封鎖的原因。",
"apihelp-query+blocks-example-simple": "列出封鎖。",
"apihelp-query+blocks-example-users": "列出使用者 <kbd>Alice</kbd> 與 <kbd>Bob</kbd> 的封鎖。",
"apihelp-query+categorymembers-paramvalue-prop-timestamp": "添加在頁面有被包含時的時間戳記。",
"apihelp-query+categorymembers-param-limit": "回傳的頁面數量上限。",
"apihelp-query+categorymembers-param-sort": "作為排序順序的屬性。",
+ "apihelp-query+categorymembers-param-start": "起始列出的時間戳記。僅能與 <kbd>$1sort=timestamp</kbd> 一起使用。",
+ "apihelp-query+categorymembers-param-end": "結束列出的時間戳記。僅能與 <kbd>$1sort=timestamp</kbd> 一起使用。",
"apihelp-query+categorymembers-param-startsortkey": "請改用 $1starthexsortkey。",
"apihelp-query+categorymembers-param-endsortkey": "請改用 $1endhexsortkey。",
"apihelp-query+categorymembers-example-simple": "取得在 <kbd>Category:Physics</kbd> 裡前 10 項的頁面。",
"apihelp-query+logevents-paramvalue-prop-userid": "添加承擔日誌事件的使用者 ID。",
"apihelp-query+logevents-paramvalue-prop-timestamp": "添加日誌事件的時間戳記。",
"apihelp-query+logevents-paramvalue-prop-comment": "添加日誌事件的註釋。",
+ "apihelp-query+logevents-paramvalue-prop-parsedcomment": "添加日誌事件的解析註釋。",
"apihelp-query+logevents-paramvalue-prop-details": "列出日誌事件的額外詳細資訊。",
"apihelp-query+logevents-paramvalue-prop-tags": "列出日誌事件的標籤。",
"apihelp-query+logevents-param-type": "篩選僅為此類型的日誌項目。",
"apihelp-query+logevents-param-tag": "僅列出以此標籤所標記的事件項目。",
"apihelp-query+logevents-param-limit": "要回傳的事件項目總數。",
"apihelp-query+logevents-example-simple": "列出近期日誌事件。",
+ "apihelp-query+pagepropnames-summary": "列出所有在 wiki 使用的頁面屬性名稱。",
"apihelp-query+pagepropnames-param-limit": "回傳的名稱數量上限。",
"apihelp-query+pagepropnames-example-simple": "取得前 10 個屬性名稱。",
"apihelp-query+pageprops-example-simple": "取得頁面 <kbd>Main Page</kbd> 與 <kbd>MediaWiki</kbd> 的屬性。",
"apihelp-query+prefixsearch-param-limit": "回傳的結果數量上限。",
"apihelp-query+prefixsearch-param-offset": "要略過的結果數量。",
"apihelp-query+prefixsearch-example-simple": "搜尋開頭為 <kbd>meaning</kbd> 的頁面標題。",
+ "apihelp-query+prefixsearch-param-profile": "搜尋要使用的配置。",
"apihelp-query+protectedtitles-param-namespace": "僅列出這些命名空間的標題。",
"apihelp-query+protectedtitles-param-level": "僅列出具有這些保護層級的標題。",
"apihelp-query+protectedtitles-param-limit": "要回傳的頁面總數。",
"apihelp-query+protectedtitles-param-prop": "要取得的屬性。",
+ "apihelp-query+protectedtitles-paramvalue-prop-user": "添加做出添加保護操作的使用者。",
+ "apihelp-query+protectedtitles-paramvalue-prop-userid": "添加做出添加保護操作的使用者 ID。",
+ "apihelp-query+protectedtitles-paramvalue-prop-comment": "添加保護的註釋。",
+ "apihelp-query+protectedtitles-paramvalue-prop-parsedcomment": "添加保護的解析註釋。",
"apihelp-query+protectedtitles-paramvalue-prop-level": "添加保護層級。",
"apihelp-query+protectedtitles-example-simple": "列出已保護的標題。",
+ "apihelp-query+querypage-summary": "取得透過特殊頁面 QueryPage-based 所提供的清單。",
"apihelp-query+querypage-param-page": "特殊頁面的名稱。註:區分大小寫。",
"apihelp-query+querypage-param-limit": "回傳的結果數量。",
+ "apihelp-query+querypage-example-ancientpages": "回傳來自 [[Special:Ancientpages]] 的結果。",
"apihelp-query+random-summary": "取得隨機頁面集合",
"apihelp-query+random-param-namespace": "僅回傳在這些命名空間的頁面。",
"apihelp-query+random-param-redirect": "請改用 <kbd>$1filterredir=redirects</kbd>。",
"apihelp-query+recentchanges-param-excludeuser": "不要列出由該使用者作出的更改。",
"apihelp-query+recentchanges-param-tag": "僅列出以此標籤所標記的更改。",
"apihelp-query+recentchanges-param-prop": "包含的額外資訊部份:",
+ "apihelp-query+recentchanges-paramvalue-prop-comment": "添加編輯的註釋。",
+ "apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "添加編輯的解析註釋。",
"apihelp-query+recentchanges-paramvalue-prop-flags": "添加編輯的標籤。",
"apihelp-query+recentchanges-paramvalue-prop-timestamp": "添加編輯的時間戳記。",
"apihelp-query+recentchanges-paramvalue-prop-title": "添加編輯的頁面標題。",
"apihelp-query+revisions-example-first5-user": "取得 <kbd>Main Page</kbd> 裡由使用者 <kbd>MediaWiki default</kbd> 所做出的最早前 5 筆修訂。",
"apihelp-query+revisions+base-paramvalue-prop-ids": "修訂 ID。",
"apihelp-query+revisions+base-paramvalue-prop-flags": "修訂標籤(小修改)。",
+ "apihelp-query+revisions+base-paramvalue-prop-timestamp": "修訂的時間戳記。",
"apihelp-query+revisions+base-paramvalue-prop-user": "做出修訂的使用者。",
+ "apihelp-query+revisions+base-paramvalue-prop-userid": "修訂創建者的使用者 ID",
"apihelp-query+revisions+base-paramvalue-prop-size": "修訂的長度(位元組)。",
+ "apihelp-query+revisions+base-paramvalue-prop-sha1": "修訂的 SHA-1(base 16)。",
"apihelp-query+revisions+base-paramvalue-prop-tags": "修訂標籤。",
"apihelp-query+revisions+base-param-limit": "限制所回傳的修訂數量。",
"apihelp-query+search-summary": "執行全文搜尋。",
"apihelp-query+search-param-limit": "要回傳的頁面總數。",
"apihelp-query+search-param-interwiki": "若可用的話,在搜尋裡包含跨 wiki 結果。",
"apihelp-query+search-param-backend": "是否搜尋使用的後端,若否則為預設。",
+ "apihelp-query+search-param-sort": "設定回傳結果的排序。",
"apihelp-query+search-example-simple": "搜尋 <kbd>meaning</kbd>。",
"apihelp-query+search-example-text": "搜尋 <kbd>meaning</kbd> 的文字。",
"apihelp-query+siteinfo-summary": "回傳有關站台的一般資訊。",
"apihelp-query+siteinfo-param-prop": "要取得的資訊:",
"apihelp-query+siteinfo-paramvalue-prop-general": "全面系統資訊。",
"apihelp-query+siteinfo-paramvalue-prop-specialpagealiases": "特殊頁面別名清單。",
+ "apihelp-query+siteinfo-param-showalldb": "列出所有資料庫伺服器,不是只有最延遲的那台。",
"apihelp-query+siteinfo-param-numberingroup": "列出在使用者群組裡的使用者數目。",
+ "apihelp-query+siteinfo-param-inlanguagecode": "用於本地化語言的語言代碼(盡可能)與外觀名稱。",
"apihelp-query+siteinfo-example-simple": "索取站台資訊。",
"apihelp-query+siteinfo-example-interwiki": "索取本地端跨 wiki 前綴的清單。",
"apihelp-query+siteinfo-example-replag": "檢查目前的響應延遲。",
"apihelp-query+usercontribs-paramvalue-prop-title": "添加標題與頁面的命名空間 ID。",
"apihelp-query+usercontribs-paramvalue-prop-timestamp": "添加編輯的時間戳記。",
"apihelp-query+usercontribs-paramvalue-prop-comment": "添加編輯的註釋。",
- "apihelp-query+usercontribs-paramvalue-prop-parsedcomment": "添加編輯的已解析註解。",
+ "apihelp-query+usercontribs-paramvalue-prop-parsedcomment": "添加編輯的解析註釋。",
"apihelp-query+usercontribs-paramvalue-prop-size": "添加編輯的新大小。",
"apihelp-query+usercontribs-paramvalue-prop-flags": "添加編輯的標籤。",
"apihelp-query+usercontribs-paramvalue-prop-patrolled": "標記已巡查編輯。",
"apihelp-query+watchlist-paramvalue-prop-user": "添加有做出編輯的使用者。",
"apihelp-query+watchlist-paramvalue-prop-userid": "添加有做出編輯的使用者 ID。",
"apihelp-query+watchlist-paramvalue-prop-comment": "添加編輯的註釋。",
- "apihelp-query+watchlist-paramvalue-prop-parsedcomment": "添加編輯的已解析註解。",
+ "apihelp-query+watchlist-paramvalue-prop-parsedcomment": "添加編輯的解析註釋。",
"apihelp-query+watchlist-paramvalue-prop-timestamp": "添加編輯的時間戳記。",
"apihelp-query+watchlist-paramvalue-prop-tags": "列出項目的標籤。",
"apihelp-query+watchlist-param-type": "要顯示的更改類型:",
"apihelp-revisiondelete-param-reason": "刪除或取消刪除的原因。",
"apihelp-revisiondelete-param-tags": "在刪除日誌裡套用到項目的標籤。",
"apihelp-rollback-summary": "撤修頁面的最後一次編輯。",
+ "apihelp-rollback-param-tags": "套用到回退的標籤。",
+ "apihelp-rollback-param-summary": "自定義編輯摘要。若為空,則使用預設摘要。",
"apihelp-rollback-param-watchlist": "無條件使用設置將頁面加入或移除目前使用者的監視清單或者是不更改監視清單。",
"apihelp-setnotificationtimestamp-param-entirewatchlist": "在所有已監視頁面運作。",
+ "apihelp-setnotificationtimestamp-example-page": "重新設定用於 <kbd>Main page</kbd> 的通知狀態。",
+ "apihelp-setnotificationtimestamp-example-allpages": "重新設定在 <kbd>{{ns:user}}</kbd> 命名空間裡頁面的通知狀態。",
"apihelp-setpagelanguage-summary": "更改頁面的語言。",
+ "apihelp-setpagelanguage-extended-description-disabled": "您不被允許在此 wiki 上變更頁面的語言。\n\n請啟用 <var>[[mw:Special:MyLanguage/Manual:$wgPageLanguageUseDB|$wgPageLanguageUseDB]]</var> 來進行此操作。",
"apihelp-setpagelanguage-param-reason": "變更的原因。",
"apihelp-setpagelanguage-example-language": "更改 <kbd>Main Page</kbd> 的語言成巴斯克語。",
"apihelp-stashedit-summary": "在分享快取裡預備編輯。",
"apihelp-unblock-param-reason": "解除封鎖的原因。",
"apihelp-unblock-param-tags": "在封鎖日誌裡更改套用到項目的標籤。",
"apihelp-unblock-example-id": "解除封銷 ID #<kbd>105</kbd>。",
+ "apihelp-undelete-summary": "恢復已刪除頁面的修訂。",
"apihelp-undelete-param-title": "要恢復的頁面標題。",
"apihelp-undelete-param-reason": "還原的原因。",
"apihelp-undelete-param-tags": "在刪除日誌裡更改套用到項目的標籤。",
"apihelp-undelete-example-page": "取消刪除頁面 <kbd>Main Page</kbd>。",
"apihelp-undelete-example-revisions": "取消刪除 <kbd>Main Page</kbd> 的兩筆修訂。",
"apihelp-unlinkaccount-summary": "移除目前使用者所連結到的第三方帳號。",
+ "apihelp-upload-summary": "上傳檔案,或取得等待上傳的狀態。",
"apihelp-upload-param-filename": "目標檔案名稱。",
"apihelp-upload-param-comment": "上傳註釋。如果 <var>$1text</var> 未指定的話,也會作為新檔案用的初始頁面文字。",
"apihelp-upload-param-text": "用於新檔案的初始頁面文字。",
"api-format-prettyprint-header-hyperlinked": "這是$1格式的HTML實現。HTML對除錯很有用,但不適合應用程式使用。\n\n指定<var>format</var>參數以更改輸出格式。要查看$1格式的非HTML實現,設置[$3 <kbd>format=$2</kbd>]。\n\n參見[[mw:API|完整文件]],或[[Special:ApiHelp/main|API幫助]]以獲取更多信息。",
"api-format-prettyprint-status": "此回應將會傳回HTTP狀態$1 $2。",
"api-login-fail-badsessionprovider": "當使用$1無法登入。",
+ "api-login-fail-sameorigin": "當未套用相同原有方針時無法登入。",
"api-pageset-param-titles": "要使用的標題清單。",
"api-pageset-param-pageids": "要使用的頁面 ID 清單。",
"api-pageset-param-revids": "要使用的修訂 ID 清單。",
"api-help-param-upload": "必須使用 multipart/form-data 以檔案上傳的方式傳送。",
"api-help-param-multi-separate": "將幾個值以 <kbd>|</kbd> 或 [[Special:ApiHelp/main#main/datatypes|alternative]] 分隔。",
"api-help-param-multi-max": "上限值為 {{PLURAL:$1|$1}} (機器人為 {{PLURAL:$2|$2}})。",
+ "api-help-param-multi-max-simple": "值的最大數量為 {{PLURAL:$1|$1}}。",
"api-help-param-multi-all": "要指定所有值,請使用<kbd>$1</kbd>。",
"api-help-param-default": "預設值:$1",
"api-help-param-default-empty": "預設值:<span class=\"apihelp-empty\">(空)</span>",
"api-help-authmanagerhelper-returnurl": "為第三方身份驗證流程傳回URL,必須為絕對值。需要此值或<var>$1continue</var>兩者之一。\n\n在接收<samp>REDIRECT</samp>回應時,一般狀況下您將打開瀏覽器或網站瀏覽功能到特定的<samp>redirecttarget</samp> URL以進行第三方身份驗證流程。當它完成時,第三方會將瀏覽器或網站瀏覽功能送至此URL。您應當提取任何來自URL的查詢或POST參數,並將之作為<var>$1continue</var>請求傳遞至此API模組。",
"api-help-authmanagerhelper-continue": "此請求是在先前的<samp>UI</samp>或<samp>REDIRECT</samp>回應之後的後續動作。必須為此值或<var>$1returnurl</var>。",
"api-help-authmanagerhelper-additional-params": "此模組允許額外參數,取決於可用的身份驗證請求。使用<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>与<kbd>amirequestsfor=$1</kbd>(或之前來自此模組的回應,如果合適)以決定可用請求及其使用的欄位。",
+ "apierror-appendnotsupported": "無法附加到使用內容模組 $1 的頁面。",
"apierror-articleexists": "您所嘗試建立的條目剛剛已被創建。",
"apierror-assertbotfailed": "斷言使用者擁有的 <code>bot</code> 權限失效。",
"apierror-assertnameduserfailed": "斷言使用者「$1」出錯。",
"apierror-contentserializationexception": "內容序列化失敗:$1",
"apierror-copyuploadbadurl": "不允許從此 URL 來上傳。",
"apierror-csp-report": "處理 CSP 報告時錯誤:$1。",
+ "apierror-emptypage": "不允許建立空白的新頁面。",
"apierror-filedoesnotexist": "檔案不存在。",
"apierror-filenopath": "無法取得本地端檔案路徑。",
"apierror-filetypecannotberotated": "無法旋轉的檔案類型。",
"apierror-imageusage-badtitle": "<kbd>$1</kbd>的標題必須是檔案。",
"apierror-import-unknownerror": "未知的匯入錯誤:$1",
"apierror-invalidcategory": "您所輸入的分類名稱無效。",
+ "apierror-invalidlang": "用於參數 <var>$1</var> 的語言代碼無效。",
+ "apierror-invalidoldimage": "<var>oldimage</var> 參數含有無效格式。",
+ "apierror-invalidparammix-cannotusewith": "參數 <kbd>$1</kbd> 不能與 <kbd>$2</kbd> 一起使用。",
+ "apierror-invalidparammix-mustusewith": "<kbd>$1</kbd> 參數僅能與 <kbd>$2</kbd> 一起使用。",
"apierror-invalidparammix": "{{PLURAL:$2|參數}} $1 不能一起使用。",
"apierror-invalidsha1base36hash": "所提供的 SHA1Base36 雜湊無效。",
"apierror-invalidsha1hash": "所提供的 SHA1 雜湊無效。",
"apierror-invalidtitle": "錯誤標題「$1」。",
+ "apierror-invalidurlparam": "<var>$1urlparam</var> 的值無效(<kbd>$2=$3</kbd>)。",
"apierror-invaliduser": "無效的使用者名稱「$1」。",
"apierror-invaliduserid": "使用者 ID <var>$1</var> 無效。",
+ "apierror-maxbytes": "參數 <var>$1</var> 不能大於 $2 {{PLURAL:$2|位元組|位元組}}",
+ "apierror-maxchars": "參數 <var>$1</var> 不能多於 $2 個{{PLURAL:$2|字元|字元}}",
+ "apierror-maxlag-generic": "正等待資料庫伺服器:已延遲 $1 {{PLURAL:$1|秒|秒}}。",
+ "apierror-maxlag": "正等待$2:已延遲 $1 {{PLURAL:$1|秒|秒}}。",
"apierror-mimesearchdisabled": "MIME 搜尋在 Miser 模式裡被停用。",
"apierror-missingcontent-pageid": "遺失頁面 ID 為 $1 的內容。",
"apierror-missingcontent-revid": "遺失修訂 ID 為 $1 的內容。",
+ "apierror-missingparam-one-of": "{{PLURAL:$2|參數|參數其一}} $1 為必要。",
"apierror-missingparam": "<var>$1</var>參數必須被設定。",
"apierror-missingrev-pageid": "沒有頁面 ID 為 $1 的目前修訂。",
"apierror-missingrev-title": "沒有標題為$1的目前修訂。",
"apierror-missingtitle": "您所指定的頁面不存在。",
"apierror-missingtitle-byname": "頁面$1不存在。",
+ "apierror-moduledisabled": "模組 <kbd>$1</kbd> 已停用。",
"apierror-mustbeloggedin-changeauth": "必須登入,才能變更身分核對資取。",
"apierror-mustbeloggedin-generic": "您必須登入。",
"apierror-mustbeloggedin-linkaccounts": "您必須登入到連結帳號。",
* made to replace information about the old content with information about
* the new content.
*
- * This default implementation calls
- * $this->getParserOutput( $content, $title, null, null, false ),
- * and then calls getSecondaryDataUpdates( $title, $recursive ) on the
- * resulting ParserOutput object.
- *
- * Subclasses may implement this to determine the necessary updates more
- * efficiently, or make use of information about the old content.
+ * @deprecated since 1.32, call and override
+ * ContentHandler::getSecondaryDataUpdates instead.
*
* @note Implementations should call the SecondaryDataUpdates hook, like
* AbstractContent does.
* the current state of the database.
*
* @since 1.21
+ * @deprecated since 1.32, call and override
+ * ContentHandler::getDeletionUpdates instead.
*
- * @param WikiPage $page The deleted page
+ * @param WikiPage $page The page the content was deleted from.
* @param ParserOutput|null $parserOutput Optional parser output object
* for efficient access to meta-information about the content object.
* Provide if you have one handy.
use Wikimedia\Assert\Assert;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
+use MediaWiki\Revision\SlotRenderingProvider;
use MediaWiki\Search\ParserOutputSearchDataExtractor;
/**
return $parserOutput;
}
+ /**
+ * Returns a list of DeferrableUpdate objects for recording information about the
+ * given Content in some secondary data store.
+ *
+ * Application logic should not call this method directly. Instead, it should call
+ * DerivedPageDataUpdater::getSecondaryDataUpdates().
+ *
+ * @note Implementations must not return a LinksUpdate instance. Instead, a LinksUpdate
+ * is created by the calling code in DerivedPageDataUpdater, on the combined ParserOutput
+ * of all slots, not for each slot individually. This is in contrast to the old
+ * getSecondaryDataUpdates method defined by AbstractContent, which returned a LinksUpdate.
+ *
+ * @note Implementations should not call $content->getParserOutput, they should call
+ * $slotOutput->getSlotRendering( $role, false ) instead if they need to access a ParserOutput
+ * of $content. This allows existing ParserOutput objects to be re-used, while avoiding
+ * creating a ParserOutput when none is needed.
+ *
+ * @param Title $title The title of the page to supply the updates for
+ * @param Content $content The content to generate data updates for.
+ * @param string $role The role (slot) in which the content is being used. Which updates
+ * are performed should generally not depend on the role the content has, but the
+ * DeferrableUpdates themselves may need to know the role, to track to which slot the
+ * data refers, and to avoid overwriting data of the same kind from another slot.
+ * @param SlotRenderingProvider $slotOutput A provider that can be used to gain access to
+ * a ParserOutput of $content by calling $slotOutput->getSlotParserOutput( $role, false ).
+ * @return DeferrableUpdate[] A list of DeferrableUpdate objects for putting information
+ * about this content object somewhere. The default implementation returns an empty
+ * array.
+ * @since 1.32
+ */
+ public function getSecondaryDataUpdates(
+ Title $title,
+ Content $content,
+ $role,
+ SlotRenderingProvider $slotOutput
+ ) {
+ return [];
+ }
+
+ /**
+ * Returns a list of DeferrableUpdate objects for removing information about content
+ * in some secondary data store. This is used when a page is deleted, and also when
+ * a slot is removed from a page.
+ *
+ * Application logic should not call this method directly. Instead, it should call
+ * WikiPage::getSecondaryDataUpdates().
+ *
+ * @note Implementations must not return a LinksDeletionUpdate instance. Instead, a
+ * LinksDeletionUpdate is created by the calling code in WikiPage.
+ * This is in contrast to the old getDeletionUpdates method defined by AbstractContent,
+ * which returned a LinksUpdate.
+ *
+ * @note Implementations should not rely on the page's current content, but rather the current
+ * state of the secondary data store.
+ *
+ * @param Title $title The title of the page to supply the updates for
+ * @param string $role The role (slot) in which the content is being used. Which updates
+ * are performed should generally not depend on the role the content has, but the
+ * DeferrableUpdates themselves may need to know the role, to track to which slot the
+ * data refers, and to avoid overwriting data of the same kind from another slot.
+ *
+ * @return DeferrableUpdate[] A list of DeferrableUpdate objects for putting information
+ * about this content object somewhere. The default implementation returns an empty
+ * array.
+ *
+ * @since 1.32
+ */
+ public function getDeletionUpdates( Title $title, $role ) {
+ return [];
+ }
+
}
* Call to WikiPage function for backwards compatibility.
* @see WikiPage::doDeleteUpdates
*/
- public function doDeleteUpdates( $id, Content $content = null ) {
- return $this->mPage->doDeleteUpdates( $id, $content );
+ public function doDeleteUpdates(
+ $id,
+ Content $content = null,
+ $revision = null,
+ User $user = null
+ ) {
+ $this->mPage->doDeleteUpdates( $id, $content, $revision, $user );
}
/**
use MediaWiki\Storage\RevisionRecord;
use MediaWiki\Storage\RevisionSlotsUpdate;
use MediaWiki\Storage\RevisionStore;
+use MediaWiki\Storage\SlotRecord;
use Wikimedia\Assert\Assert;
use Wikimedia\Rdbms\FakeResultWrapper;
use Wikimedia\Rdbms\IDatabase;
* Do some database updates after deletion
*
* @param int $id The page_id value of the page being deleted
- * @param Content|null $content Optional page content to be used when determining
+ * @param Content|null $content Page content to be used when determining
* the required updates. This may be needed because $this->getContent()
* may already return null when the page proper was deleted.
- * @param Revision|null $revision The latest page revision
+ * @param RevisionRecord|Revision|null $revision The current page revision at the time of
+ * deletion, used when determining the required updates. This may be needed because
+ * $this->getRevision() may already return null when the page proper was deleted.
* @param User|null $user The user that caused the deletion
*/
public function doDeleteUpdates(
$id, Content $content = null, Revision $revision = null, User $user = null
) {
+ if ( $id !== $this->getId() ) {
+ throw new InvalidArgumentException( 'Mismatching page ID' );
+ }
+
try {
$countable = $this->isCountable();
} catch ( Exception $ex ) {
) );
// Delete pagelinks, update secondary indexes, etc
- $updates = $this->getDeletionUpdates( $content );
+ $updates = $this->getDeletionUpdates(
+ $revision ? $revision->getRevisionRecord() : $content
+ );
foreach ( $updates as $update ) {
DeferredUpdates::addUpdate( $update );
}
* updates should remove any information about this page from secondary data
* stores such as links tables.
*
- * @param Content|null $content Optional Content object for determining the
- * necessary updates.
+ * @param RevisionRecord|Content|null $rev The revision being deleted. Also accepts a Content
+ * object for backwards compatibility.
* @return DeferrableUpdate[]
*/
- public function getDeletionUpdates( Content $content = null ) {
- if ( !$content ) {
- // load content object, which may be used to determine the necessary updates.
- // XXX: the content may not be needed to determine the updates.
+ public function getDeletionUpdates( $rev = null ) {
+ if ( !$rev ) {
+ wfDeprecated( __METHOD__ . ' without a RevisionRecord', '1.32' );
+
try {
- $content = $this->getContent( Revision::RAW );
+ $rev = $this->getRevisionRecord();
} catch ( Exception $ex ) {
// If we can't load the content, something is wrong. Perhaps that's why
// the user is trying to delete the page, so let's not fail in that case.
// Note that doDeleteArticleReal() will already have logged an issue with
// loading the content.
+ wfDebug( __METHOD__ . ' failed to load current revision of page ' . $this->getId() );
}
}
- if ( !$content ) {
- $updates = [];
+ if ( !$rev ) {
+ $slotContent = [];
+ } elseif ( $rev instanceof Content ) {
+ wfDeprecated( __METHOD__ . ' with a Content object instead of a RevisionRecord', '1.32' );
+
+ $slotContent = [ 'main' => $rev ];
} else {
- $updates = $content->getDeletionUpdates( $this );
+ $slotContent = array_map( function ( SlotRecord $slot ) {
+ return $slot->getContent( Revision::RAW );
+ }, $rev->getSlots()->getSlots() );
}
- Hooks::run( 'WikiPageDeletionUpdates', [ $this, $content, &$updates ] );
- return $updates;
+ $allUpdates = [ new LinksDeletionUpdate( $this ) ];
+
+ // NOTE: once Content::getDeletionUpdates() is removed, we only need to content
+ // model here, not the content object!
+ // TODO: consolidate with similar logic in DerivedPageDataUpdater::getSecondaryDataUpdates()
+ /** @var Content $content */
+ foreach ( $slotContent as $role => $content ) {
+ $handler = $content->getContentHandler();
+
+ $updates = $handler->getDeletionUpdates(
+ $this->getTitle(),
+ $role
+ );
+ $allUpdates = array_merge( $allUpdates, $updates );
+
+ // TODO: remove B/C hack in 1.32!
+ $legacyUpdates = $content->getDeletionUpdates( $this );
+
+ // HACK: filter out redundant and incomplete LinksDeletionUpdate
+ $legacyUpdates = array_filter( $legacyUpdates, function ( $update ) {
+ return !( $update instanceof LinksDeletionUpdate );
+ } );
+
+ $allUpdates = array_merge( $allUpdates, $legacyUpdates );
+ }
+
+ Hooks::run( 'PageDeletionDataUpdates', [ $this->getTitle(), $rev, &$allUpdates ] );
+
+ // TODO: hard deprecate old hook in 1.33
+ Hooks::run( 'WikiPageDeletionUpdates', [ $this, $content, &$allUpdates ] );
+ return $allUpdates;
}
/**
);
}
- /**
- * Returns JS code which calls the script given by $script. The script will
- * be called with local variables name, version, dependencies and group,
- * which will have values corresponding to $name, $version, $dependencies
- * and $group as supplied.
- *
- * @param string $name Module name
- * @param string $version Module version hash
- * @param array $dependencies List of module names on which this module depends
- * @param string $group Group which the module is in.
- * @param string $source Source of the module, or 'local' if not foreign.
- * @param string $script JavaScript code
- * @return string JavaScript code
- */
- public static function makeCustomLoaderScript( $name, $version, $dependencies,
- $group, $source, $script
- ) {
- $script = str_replace( "\n", "\n\t", trim( $script ) );
- return Xml::encodeJsCall(
- "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )",
- [ $name, $version, $dependencies, $group, $source ],
- self::inDebugMode()
- );
- }
-
private static function isEmptyObject( stdClass $obj ) {
foreach ( $obj as $key => $value ) {
return false;
"redirect-file": "اسم ملف",
"redirect-logid": "معرف السجل",
"redirect-not-exists": "المطلوب غير موجود",
+ "redirect-not-numeric": "قيمة غير رقمية",
"fileduplicatesearch": "بحث عن ملفات مكررة",
"fileduplicatesearch-summary": "ابحث عن الملفات المكررة بناء على قيم الهاش.",
"fileduplicatesearch-filename": "اسم الملف:",
"customcssprotected": "Nun tienes permisu pa editar esta páxina CSS porque contien preferencies personales d'otru usuariu.",
"customjsonprotected": "Nun tienes permisu pa editar esta páxina JSON porque contien preferencies personales d'otru usuariu.",
"customjsprotected": "Nun tienes permisu pa editar esta páxina de JavaScript porque contien preferencies personales d'otru usuariu.",
- "sitecssprotected": "Nun tienes permisu pa editar esta páxina de CSS porque puede afeutar a tolos visitantes",
- "sitejsonprotected": "Nun tienes permisu pa editar esta páxina en JSON porque puede afeutar a tolos visitantes",
- "sitejsprotected": "Nun tienes permisu para editar esta páxina de JavaScript porque puede afeutar a tolos visitantes",
+ "sitecssprotected": "Nun tienes permisu pa editar esta páxina de CSS porque puede afeutar a tolos visitantes.",
+ "sitejsonprotected": "Nun tienes permisu pa editar esta páxina en JSON porque puede afeutar a tolos visitantes.",
+ "sitejsprotected": "Nun tienes permisu para editar esta páxina de JavaScript porque puede afeutar a tolos visitantes.",
"mycustomcssprotected": "Nun tien permisu pa editar esta páxina CSS.",
"mycustomjsonprotected": "Nun tien permisu pa editar esta páxina JSON.",
"mycustomjsprotected": "Nun tien permisu pa editar esta páxina JavaScript.",
"redirect-file": "Nome del ficheru",
"redirect-logid": "ID del rexistru",
"redirect-not-exists": "Nun s'alcontró'l valor",
+ "redirect-not-numeric": "El valor nun ye un númberu",
"fileduplicatesearch": "Buscar archivos duplicaos",
"fileduplicatesearch-summary": "Busca archivos duplicaos basándose nos sos valores fragmentarios.",
"fileduplicatesearch-filename": "Nome del ficheru:",
"edit-error-long": "Errores:\n\n$1",
"revid": "revisión $1",
"pageid": "ID de páxina $1",
- "interfaceadmin-info": "$1\n\nLos permisos pa editar los ficheros CSS, JS y JSON globales del sitiu fueron apocayá dixebraos del permisu <code>editinterface</code>. Si nun entiendes por qué recibes esti error, por favor llei [[mw:MediaWiki_1.32/interface-admin]].",
+ "interfaceadmin-info": "$1\n\nLos permisos pa editar los ficheros CSS, JS y JSON globales del sitiu dixebráronse apocayá del permisu <code>editinterface</code>. Si nun entiendes por qué recibes esti error, llei [[mw:MediaWiki_1.32/interface-admin]].",
"rawhtml-notallowed": "Les etiquetes <html> nun pueden usase fuera de les páxines normales.",
"gotointerwiki": "Dexando {{SITENAME}}",
"gotointerwiki-invalid": "El títulu especificáu nun ye válidu.",
"currentrev": "最新版本",
"currentrev-asof": "$1 muōi-muōi siŏh-huôi biĕng-cĭk",
"revisionasof": "$1 gì bēng-buōng",
+ "revision-info": "Găk $1 iù {{GENDER:$6|$2}} có̤ gì biĕng-cĭk $7",
"previousrevision": "← Gá-gô gì bēng-buōng",
"nextrevision": "加新其版本→",
"currentrevisionlink": "最新版本",
"yourrealname": "真實姓名:",
"yourlanguage": "語言:",
"yournick": "新其簽名:",
+ "badsig": "Nguòng-sṳ̄ chiĕng-miàng ô dâng.\nGái káng-lâ HTML biēu-chiĕng.",
"email": "電批",
"prefs-help-email": "電子郵件地址是愛寫就寫其,但是如果汝𣍐記密碼咯,密碼重置其時候需要茲。",
"prefs-help-email-others": "汝也會使選擇讓其他其用戶通過汝其用戶頁面或者討論頁面懸頂其鏈接,使電子郵件來聯繫汝。其他其用戶聯繫汝其辰候,汝其電子郵件地址𣍐顯示出來。",
"boteditletter": "^",
"rc-change-size-new": "Siŭ-gāi ī-hâiu biéng có̤ $1 cê-ciék",
"rc-enhanced-hide": "囥起細節",
+ "rc-old-title": "Kī-tàu hô̤ lā̤ „$1“",
"recentchangeslinked": "相關其改變",
"recentchangeslinked-feed": "相關其改變",
"recentchangeslinked-toolbox": "Sŏng-guăng gì gāi-biéng",
"deletecomment": "原因:",
"rollback": "再修改轉去",
"rollbacklink": "duōng",
+ "rollbacklinkcount": "Huòi-tó̤i $1 huòi biĕng-cĭk",
"rollbackfailed": "轉𣍐去",
"cantrollback": "𣍐使恢復修改;最後其貢獻者是茲蜀頁其唯一其作者。",
"alreadyrolled": "𣍐使回滾最後蜀回[[User:$2|$2]] ([[User talk:$2|討論]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])其[[:$1]]編輯;\n有其他儂已經編輯過了或者茲蜀頁已經乞回滾過了。\n\n最後蜀回茲蜀頁其修改是[[User:$3|$3]] ([[User talk:$3|討論]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])改其。",
"tooltip-feed-atom": "Cī-siŏh-hiĕk gì Atom lài-nguòng",
"tooltip-t-contributions": "{{GENDER:$1|茲蜀隻用戶}}其貢獻單單",
"tooltip-t-emailuser": "向{{GENDER:$1|茲蜀隻用戶}}寄電批",
+ "tooltip-t-info": "Cī-siŏh-hiĕk gì gó-sâ̤ séng-sék",
"tooltip-t-upload": "Siông-diòng ùng-giông",
"tooltip-t-specialpages": "Cuòng-buô dĕk-sṳ̀-hiĕk dăng-dăng",
"tooltip-t-print": "Cī-hiĕk â̤ páh-éng gì bēng-buōng",
"watchlisttools-view": "看相關改變",
"watchlisttools-edit": "看共修改監視單",
"watchlisttools-raw": "修改原始監視單",
+ "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|tō̤-lâung]])",
"redirect": "Áng ùng-giông, ê̤ṳng-hô, hiĕk-miêng siŭ-gāi, nĭk-cé ID kó̤ tṳ̀ng-dêng-hióng",
"redirect-submit": "Kó̤",
"redirect-lookup": "Sìng-tō̤:",
"redirect-file": "Datei",
"redirect-logid": "Logbucheintrag",
"redirect-not-exists": "Der Wert wurde nicht gefunden",
+ "redirect-not-numeric": "Der Wert ist nicht numerisch",
"fileduplicatesearch": "Dateiduplikatsuche",
"fileduplicatesearch-summary": "Suche nach Dateiduplikaten auf Basis ihres Hashwertes.",
"fileduplicatesearch-filename": "Dateiname:",
"filehist-comment": "Megjegyzés",
"imagelinks": "Fájlhasználat",
"linkstoimage": "Az alábbi {{PLURAL:$1|lap használja|lapok használják}} ezt a fájlt:",
- "linkstoimage-more": "Több mint $1 oldal használja ezt a fájlt.\nA következő lista csak az {{PLURAL:$1|első lapot mutatja, ami használja a képet|első $1 lapot mutatja, amelyek használják a képet}}.\nA teljes lista [[Special:WhatLinksHere/$2|ezen a lapon]] található meg.",
+ "linkstoimage-more": "Több mint $1 oldal használja ezt a fájlt.\nA következő lista csak a fájlt használó {{PLURAL:$1|első|első $1}} lapot mutatja.\nA teljes lista [[Special:WhatLinksHere/$2|ezen a lapon]] található meg.",
"nolinkstoimage": "Ezt a fájlt nem használja egyetlen lap sem.",
"morelinkstoimage": "[[Special:WhatLinksHere/$1|További hivatkozások]] megtekintése",
"linkstoimage-redirect": "$1 (fájlátirányítás) $2",
"confirm-rollback-top": "Visszavonod a változtatásokat?",
"confirm-mcrundo-title": "Egy változtatás visszavonva",
"mcrundofailed": "A visszavonás nem sikerült",
+ "mcrundo-missingparam": "Kötelező paraméterek hiányoznak a kérésből.",
"ellipsis": "…",
"quotation-marks": "„$1”",
"imgmultipageprev": "← előző oldal",
"edit-error-long": "Hibák:\n\n$1",
"revid": "$1 változat",
"pageid": "$1 lapazonosító",
+ "interfaceadmin-info": "$1\n\nA CSS/JS/JSON lapok szerkesztéséhez szükséges jogosultság a közelmúltban elválasztásra került a <code>editinterface</code> jogtól. Amennyiben nem érted, miért látod ezt az üzenetet, [[mw:MediaWiki_1.32/interface-admin|itt tudhatsz meg többet]].",
"rawhtml-notallowed": "<html> címkék nem használhatók normál lapokon kívül.",
"gotointerwiki": "{{SITENAME}} elhagyása",
"gotointerwiki-invalid": "A megadott cím érvénytelen.",
"customcssprotected": "Vu ne es permisita redaktar ita CSS pagino nam ol kontenas personal ajustaji di altra uzero.",
"customjsonprotected": "Vu ne havas permiso por redaktar ica pagino JSON pro ke ol kontenas personal ajustaji di altra uzero.",
"customjsprotected": "Vu ne es permisita redaktar ita JavaScript pagino nam ol kontenas personal ajustaji di altra uzero.",
+ "sitecssprotected": "Vu ne havas permiso pri redaktar ica pagino CSS pro ol povus afektor omna viziteri.",
+ "sitejsonprotected": "Vu ne povas redaktar ica JSON (JavaScript Object Notation), pro ol povas afektar omna viziteri.",
"sitejsprotected": "Vu ne povas redaktar ica JavaScript, pro ol povas afektar omna viziteri.",
"mycustomcssprotected": "Vu ne es permisita redaktar ita CSS pagino.",
"mycustomjsonprotected": "Vu ne es permisita redaktar ita pagino JSON.",
"botpasswords-label-delete": "Efacar",
"botpasswords-label-resetpassword": "Sendez nova pasovorto per e-posto",
"botpasswords-label-grants": "Uzebla grantaji:",
+ "botpasswords-help-grants": "Permisas aceso al yuri quin vua uzero-konto ja havas. Permisar ulo hike furnisos nula altra aceso por altra yuri quin vua konto altramaniere ne havus. Videz la [[Special:ListGrants|tabelo pri permisi]] por plusa informi.",
"botpasswords-label-grants-column": "Permisita",
"botpasswords-bad-appid": "La nomo \"$1\" por la bot-programo esas nevalida.",
"botpasswords-insert-failed": "L'adjunto di nova nomo \"$1\" por la 'bot' faliis. Ka ol ja adjuntesis?",
+ "botpasswords-update-failed": "Ne povis rinomizar la 'bot' nomizita \"$1\". Kad ol efacesis?",
"botpasswords-created-title": "Kreita pasovorto por la 'bot'",
"botpasswords-created-body": "La pasovorto por la 'bot' nomizita \"$1\" del {{GENDER:$2|uzero}} \"$2\" kreesis.",
"botpasswords-updated-title": "La pasovorto dil 'bot' aktualigesis",
"botpasswords-updated-body": "La pasovorto por la 'bot' nomizita \"$1\" del {{GENDER:$2|uzero}} \"$2\" kreesis.",
"botpasswords-deleted-title": "La pasovorto por la 'bot' efacesis",
+ "botpasswords-deleted-body": "La pasovorto por la 'bot' nomizita \"$1\" del {{GENDER:$2|uzero}} \"$2\" kreesis.",
+ "botpasswords-not-exist": "L'uzero \"$1\" ne havas pasovorto nomizita \"$2\" por lua 'bot'.",
+ "botpasswords-needs-reset": "La pasovorto por la 'bot' nomizita \"$1\" dal {{GENDER:$2|uzero}} \"$2\" mustas rikreesar.",
"resetpass_forbidden": "La pasovorti ne povas chanjesar",
"resetpass_forbidden-reason": "Pasovorti ne povas chanjesar: $1",
"resetpass-no-info": "Vu mustas enirar la konto por acesar ita pagino direte.",
"passwordreset": "Sendez nova pasovorto per e-posto",
"passwordreset-text-one": "Garnisez ica formulario por recevar provizora pasovorto per vua e-posto.",
"passwordreset-text-many": "{{PLURAL:$1|Skribez en un ek la texto-buxi por recevar tempala pasovorto per e-posto.}}",
+ "passwordreset-emaildisabled": "La funcioni di e-posto (e-mail) blokusesis en ica Wiki.",
"passwordreset-username": "Uzantonomo:",
"passwordreset-domain": "Interreto-domeno:",
"passwordreset-email": "E-postal adreso:",
"passwordreset-emailtitle": "Detali pri la konto en {{SITENAME}}",
"passwordreset-emailtext-ip": "Ulu (probable vu, de la IP-adresO $1) demandis la remplaso di la pasovorto por {{SITENAME}} ($4). La sequanta {{PLURAL:$3|konto|konti}} esas asociita kun ta adreso di e-posto:\n\n$2\n\nIca tempala {{PLURAL:$3| pasovorto|pasovorti}} perdos la valideso pos {{PLURAL:$5|un dio|$5 dii}}.\nTu mustas facar 'log in' e selektar nova pasovorto nemediate. Se altra persono facis ica demando, o se vu rimemoris l'antea pasovorto e ne pluse bezonas modifikor ol, vu povas ignorar ica mesajo e durar l'uzo dil antea pasovorto.",
+ "passwordreset-emailtext-user": "Ulu (posible vu, de la IP-adreso $1) demandis la remplaso di la pasovorto por {{SITENAME}} ($4). La sequanta {{PLURAL:$3|konto|konti}} esas asociita kun ta adreso di e-posto:\n\n$2\n\nIca tempala {{PLURAL:$3| pasovorto|pasovorti}} perdos la valideso pos {{PLURAL:$5|un dio|$5 dii}}.\nTu mustas facar 'log in' e selektar nova pasovorto nemediate. Se altra persono facis ica demando, o se vu rimemoris l'antea pasovorto e ne pluse bezonas modifikor ol, vu povas ignorar ica mesajo e durar l'uzo dil antea pasovorto.",
"passwordreset-emailelement": "Uzantonomo:\n$1\n\nProvizora pasovorto:\n$2",
"passwordreset-emailsentemail": "Se ica e-posto esas asociita kun vua konto, do la nova pasovorto sendesos a vu per e-posto.",
+ "passwordreset-emailsentusername": "Se ica e-posto esas asociita kun vua konto, do la nova pasovorto sendesos a vu per e-posto.",
"passwordreset-nocaller": "Ula demandero mustas furnisesar",
"passwordreset-nosuchcaller": "La demandero ne existas: $1",
"passwordreset-invalidemail": "Ne-valida e-posto-adreso",
"noarticletext-nopermission": "Til nun ne existas texto en ica pagino.\nVu povas [[Special:Search/{{PAGENAME}}|serchar ica titulo]] en altra pagini, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchar en la relata registri], o [{{fullurl:{{FULLPAGENAME}}|action=edit}} redaktar ica pagino]</span>, tamen vu ne havas permiso por krear ica pagino.",
"userpage-userdoesnotexist": "Uzeronomo \"$1\" ne registragesis.\nVoluntez konfirmar se vu volas krear/redaktar ica pagino.",
"userpage-userdoesnotexist-view": "L'uzeronomo \"$1\" ne enrejistresis.",
+ "blocked-notice-logextract": "Ica uzero nun esas blokusita.\nLa lasta protokolo pri blokuso esas videbla adinfre, por refero:",
"clearyourcache": "<strong>Atencez:</strong> Pos registragar, vu probable mustas renovigar la tempala-magazino di vua navigilo por vidar la chanji.\n* <strong>Firefox / Safari:</strong>Tenez <em>Shift</em> kliktante <em>Reload</em>, o presez sive <em>Ctrl-F5</em> sive <em>Ctrl-R</em> (<em>⌘-R</em> ye Mac);\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> en komputeri Mac)\n* <strong>Internet Explorer:</strong> Tenez <em>Ctrl</em> kliktante <em>Refresh</em>, o presez <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Irez a <em>Menu → Settings</em> (<em>Opera → Preferences</em> ye komputeri Mac) e pose a <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
+ "usercssyoucanpreview": "<strong>Sugestajo:</strong> Uzez la butono \"{{int:showpreview}}\" por probar vua nova stilo CSS ante konservar ol.",
+ "userjsonyoucanpreview": "<strong>Sugestajo:</strong> Uzes la butono \"{{int:showpreview}}\" por provar vua nova JSON ante konservar ol.",
+ "userjsyoucanpreview": "<strong>Sugestajo:</strong> Uzes la butono \"{{int:showpreview}}\" por probar vua JavaScript ante konservar ol.",
"usercsspreview": "'''Memorez ke vu nur previdas vua uzero-CSS.'''\n'''Ol ne registragesis ankore!'''",
+ "userjsonpreview": "<strong>Memorez ke vu nur previdas vua JSON (JavaScript Object Notation). Ol ankore ne konservesis!</strong>",
"userjspreview": "'''Memorez ke vu nur previdas vua JavaScript di uzero. Ol ne registragesis ankore!'''",
+ "sitecsspreview": "<strong>Memorez ke vu nur previdas vua uzero-CSS. Ol ankore ne konservesis!</strong>",
"updated": "(Aktualigita)",
"note": "'''Noto:'''",
"previewnote": "<strong>Atencez ke ico esas nur prevido.</strong> Ol ne registragesis ankore!",
"grouppage-interface-admin": "{{ns:project}}:Администратори на посредникот",
"grouppage-bureaucrat": "{{ns:project}}:Бирократи",
"grouppage-suppress": "{{ns:project}}:Притајување",
- "right-read": "Читање страници",
+ "right-read": "Читање на страници",
"right-edit": "Уредување на страници",
"right-createpage": "Создавање на страници (кои не се разговорни страници)",
"right-createtalk": "Создавање на разговорни страници",
"right-createaccount": "Создавање на нови кориснички сметки",
"right-autocreateaccount": "Автоматска најава со надворешна корисничка сметка",
"right-minoredit": "Означување на уредувањата како ситни",
- "right-move": "Преместување страници",
+ "right-move": "Преместување на страници",
"right-move-subpages": "Преместување на страници со нивните потстраници",
"right-move-rootuserpages": "Преместување на основни кориснички страници",
"right-move-categorypages": "Преместување на категориски страници",
"@metadata": {
"authors": [
"Dr Lotus Black",
- "Htawmonzel"
+ "Htawmonzel",
+ "Aue Nai"
]
},
"tog-underline": "လေန် မတာပၞောန်သၟဝ်",
"tog-hideminor": "ပၞုက် အရာမပလေဝ်ဒါန်လဝ် ကိစ္စဟွံဇၞော် နူကဵု ပြဟ်ဟ်ဏအ်",
"tog-hidepatrolled": "ပၞုက် အရာမပလေဝ်ဒါန်လဝ် ကိစ္စဇၞော်ဇၞော် နူကဵု ပြဟ်ဟ်ဏအ်",
"tog-hidecategorization": "ပၞုက် အရာမဖျေဟ်ကဏ္ဍ ကုမုက်လိက်",
+ "tog-showhiddencats": "ထ္ၜးကဆံၚ်မပၞုက်လဝ်",
+ "tog-prefershttps": "လၟိုန်သုၚ်စောဲကေတ် လာၚ်မမၞုံဂီုကၠီု အခိၚ်မလုပ်လံက်အေန်",
+ "underline-always": "လၟိုန်အခါ",
+ "underline-never": "မွဲလှေ်ဟွံမွဲ",
"sunday": "တ္ၚဲအဒိုက်",
"monday": "တ္ၚဲစန်",
"tuesday": "တ္ၚဲအၚာ",
"oct": "အံက်",
"nov": "နဝ်",
"dec": "ဒဳ",
+ "january-date": "ဇန်နဝါရဳ $1",
+ "march-date": "မာတ် $1",
+ "april-date": "ဨပရဳ $1",
+ "may-date": "မေ $1",
+ "june-date": "ဂျေန် $1",
+ "july-date": "ဂျူလာၚ် $1",
+ "august-date": "သြဂုတ် $1",
+ "october-date": "အံက်တိုဘာ $1",
+ "november-date": "နိုဝေန်ဘာ$1",
+ "period-am": "နူဂယး",
+ "period-pm": "သဝ်တ္ၚဲ",
"pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍဂမၠိုင်}}",
"category_header": "မုက်လိက်ဂမၠိုင် ပ္ဍဲ ကဏ္ဍ \"$1\"",
"subcategories": "ကဏ္ဍလစှ်ေဂမၠိုင်",
"category-media-header": "မဳဒဳယာ စပ်ကဵု ကဏ္ဍ \"$1\"",
"category-empty": "<em>ကဏ္ဍဏအ် ပြဟ်ဟ်ဏအ် ဟွံမဲ ကု မုက်လိက် ဟွံသေင်မ္ဂး ပရူမွဲမွဲဏီရ</em>",
"hidden-categories": "{{PLURAL:$1|ကဏ္ဍ မပၞုက်|ကဏ္ဍ မပၞုက်ဂမၠိုင်}}",
+ "hidden-category-category": "ကဆံၚ် မပၞုက်လဝ်",
"category-subcat-count": "{{PLURAL:$2|ကဏ္ဍဏအ်ဂှ် ကဏ္ဍလစှ်ေ နွံဆ အတိုင်ဗွဲသၟဝ်ဝွံရ၊၊|ကဏ္ဍဏအ် နွံဆအတိုင် ဗွဲသၟဝ်ဝွံရ၊၊ {{PLURAL:$1|ကဏ္ဍလစှ်ေ|$1 ကဏ္ဍလစှ်ေဂမၠိုင်}}, ပၞောဝ်ကဵု $2 သီုဖအိုတ်}}",
+ "category-subcat-count-limited": "ကဆံၚ်(အဇာ)ဝွံ နွံအတိုၚ်ဗွဲသၟဝ် {{PLURAL:$1|ကဆံၚ်ပါ်|$1ကဆံၚ်ပါ်ဂမၠိုၚ်}}",
"category-article-count": "{{PLURAL:$2|ကဏ္ဍဏအ် နွံဆၜိုတ် အတိုင်ဗွဲသၟဝ်ဏအ်ရ.| ဗွဲသၟဝ်ဏအ်ဂှ် {{PLURAL:$1| ဒှ်လၟိဟ် မုက်လိက်| ဒှ်လၟိဟ် မုက်လိက်ဂမၠိုင် $1 }} စပ် ကုကဏ္ဍဏအ်ရ၊၊ သီုဖအိုတ် နွံ $2}}",
+ "category-article-count-limited": "အတိုၚ်ဗွဲသၟဝ်{{PLURAL:$1|မုက်ဂှ်|$1 မုက်တအ်}} ပ္ဍဲကဆံၚ်မလၟုဟ်.",
"category-file-count": "{{PLURAL:$2|ကဏ္ဍဏအ် နွံဆၜိုတ် ဝှာင်|အတိုင် ဗွဲသၟဝ်ဏအ်ရ {{PLURAL:$1|ဝှာင် ဂှ်|$1 ဝှာင်တအ် ဂှ်}} ပ္ဍဲကဏ္ဍဏအ် သီုဖအိုတ် နွံ $2 ၊၊}}",
"listingcontinuesabbrev": "ဆက်",
"noindex-category": "မုက်လိက် မသက္ကုလိက်",
"redirect-file": "Bestandsnaam",
"redirect-logid": "Logboekregel-ID",
"redirect-not-exists": "Waarde niet gevonden",
+ "redirect-not-numeric": "Waarde is geen nummer",
"fileduplicatesearch": "Duplicaatbestanden zoeken",
"fileduplicatesearch-summary": "Duplicaatbestanden zoeken op basis van de hashwaarde.",
"fileduplicatesearch-filename": "Bestandsnaam:",
"right-suppressionlog": "Podgląd rejestru ukrywania",
"right-block": "Blokowanie użytkownikom możliwości edycji",
"right-blockemail": "Blokowanie użytkownikom możliwości wysyłania wiadomości",
- "right-hideuser": "Blokowanie użytkownika, niewidoczne publicznie",
+ "right-hideuser": "Blokowanie użytkownika i ukrywanie od publiczności",
"right-ipblock-exempt": "Obejście blokad, automatycznych blokad i blokad zakresów adresów IP",
"right-unblockself": "Odblokowanie samego siebie",
"right-protect": "Zmiana poziomu zabezpieczenia i edycja stron zabezpieczonych kaskadowo",
"redirect-file": "Nome do arquivo",
"redirect-logid": "ID de log",
"redirect-not-exists": "Valor não encontrado",
+ "redirect-not-numeric": "Valor não numérico",
"fileduplicatesearch": "Procurar por arquivos duplicados",
"fileduplicatesearch-summary": "Procure por arquivos duplicados tendo por base seu valor \"hash\".",
"fileduplicatesearch-filename": "Nome do arquivo:",
"redirect-file": "Название файла",
"redirect-logid": "ID журнала",
"redirect-not-exists": "Значение не найдено",
+ "redirect-not-numeric": "Значение не числовое",
"fileduplicatesearch": "Поиск одинаковых файлов",
"fileduplicatesearch-summary": "Поиск одинаковых файлов по хэш-коду.",
"fileduplicatesearch-filename": "Имя файла:",
"redirect-file": "Ime datoteke",
"redirect-logid": "ID dnevnika",
"redirect-not-exists": "Vrednosti ni mogoče najti",
+ "redirect-not-numeric": "Vrednost ni številska",
"fileduplicatesearch": "Iskanje podvojenih datotek",
"fileduplicatesearch-summary": "Iskanje podvojenih datotek, ki temelji na podlagi njenih hashvrednosti.",
"fileduplicatesearch-filename": "Ime datoteke:",
"redirect-file": "Назив датотеке",
"redirect-logid": "ID дневника",
"redirect-not-exists": "Вредност није пронађена",
+ "redirect-not-numeric": "Вредност није нумеричка",
"fileduplicatesearch": "Претрага дупликата датотека",
"fileduplicatesearch-summary": "Претрага дуплираних датотека према хеш вредности.",
"fileduplicatesearch-filename": "Назив датотеке:",
"permissionserrorstext-withaction": "由於下列{{PLURAL:$1|原因}},您沒有權限進行 $2 的動作:",
"contentmodelediterror": "您無法編輯此修訂,因為它的內容模型為<code>$1</code>,與目前使用的頁面內容模型<code>$2</code>不同。",
"recreate-moveddeleted-warn": "<strong>警告:您正重新建立先前已刪除的頁面。</strong>\n\n您應考慮是否繼續編輯此頁。\n在此提供刪除與移動日誌方便作為參考:",
- "moveddeleted-notice": "此頁面已刪除。\n下方提供此頁面的刪除、保護和移動日誌以便參考。",
+ "moveddeleted-notice": "此頁面已遭刪除。\n請參考下方關於此頁面的刪除、保護和移動日誌。",
"moveddeleted-notice-recent": "對不起,此頁面剛剛被刪除(在最近24小時內)。頁面的刪除、保護和移動日誌在下方提供以供參考。",
"log-fulllog": "檢視完整日誌",
"edit-hook-aborted": "編輯已被 Hook 中止。\n且未回應無任何說明。",
"redirect-file": "檔案名稱",
"redirect-logid": "日誌 ID",
"redirect-not-exists": "查無值",
+ "redirect-not-numeric": "值不是數字",
"fileduplicatesearch": "搜尋重複檔案",
"fileduplicatesearch-summary": "依據雜湊值 (Hash) 來搜尋重複的檔案。",
"fileduplicatesearch-filename": "檔案名稱:",
/* global extDependencyMap */
-( function ( $ ) {
+( function () {
$( function () {
var $label, labelText;
} );
} );
} );
-}( jQuery ) );
+}() );
*
* @class jQuery.plugin.spinner
*/
-( function ( $ ) {
+( function () {
// Default options for new spinners,
// stored outside the function to share between calls.
* @mixins jQuery.plugin.spinner
*/
-}( jQuery ) );
+}() );
*
* @author Christian Bach/christian.bach@polyester.se
*/
-( function ( $, mw ) {
+( function () {
var ts,
parsers = [];
type: 'numeric'
} );
-}( jQuery, mediaWiki ) );
+}() );
// * This installation of tipsy includes several local modifications to both Javascript and CSS.
// Please be careful when upgrading.
-( function ( mw, $ ) {
+( function () {
function maybeCall(thing, ctx) {
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
}
};
-}( mediaWiki, jQuery ) );
+}() );
*
* @class jQuery.plugin.accessKeyLabel
*/
-( function ( $, mw ) {
+( function () {
// Cached access key modifiers for used browser
var cachedAccessKeyModifiers,
* @mixins jQuery.plugin.accessKeyLabel
*/
-}( jQuery, mediaWiki ) );
+}() );
* @param {string} str
* @return {number}
*/
-mediaWiki.log.deprecate( jQuery, 'byteLength', require( 'mediawiki.String' ).byteLength,
+mw.log.deprecate( $, 'byteLength', require( 'mediawiki.String' ).byteLength,
'Use require( \'mediawiki.String\' ).byteLength instead.', '$.byteLength' );
/**
/**
* @class jQuery.plugin.checkboxShiftClick
*/
-( function ( $ ) {
+( function () {
/**
* Enable checkboxes to be checked or unchecked in a row by clicking one,
* @mixins jQuery.plugin.checkboxShiftClick
*/
-}( jQuery ) );
+}() );
*
* - 2011-01-05: Forked for MediaWiki. See also jQuery.colorUtil plugin
*/
-( function ( $ ) {
+( function () {
function getColor( elem, attr ) {
var color;
};
} );
-}( jQuery ) );
+}() );
* Mostly based on other plugins and functions (linted and optimized a little).
* Sources cited inline.
*/
-( function ( $ ) {
+( function () {
/**
* @class jQuery.colorUtil
* @singleton
};
-}( jQuery ) );
+}() );
*
* @class jQuery.plugin.confirmable
*/
-( function ( $ ) {
+( function () {
var identity = function ( data ) {
return data;
};
noTitle: undefined
}
};
-}( jQuery ) );
+}() );
* This file serves to inject our localised messages into it.
*/
-( function ( mw, $ ) {
+( function () {
$.fn.confirmable.defaultOptions.i18n = {
space: mw.message( 'word-separator' ).text(),
confirm: mw.message( 'confirmable-confirm', mw.user ).text(),
yesTitle: undefined,
noTitle: undefined
};
-}( mediaWiki, jQuery ) );
+}() );
/**
* @class jQuery.plugin.getAttrs
*/
-( function ( $ ) {
+( function () {
function serializeControls( controls ) {
var i,
data = {},
$.fn.serializeObject = function () {
return serializeControls( this.serializeArray() );
};
-}( jQuery ) );
+}() );
*
* @class jQuery.plugin.hidpi
*/
-( function ( $ ) {
+( function () {
/**
* Get reported or approximate device pixel ratio.
* @mixins jQuery.plugin.hidpi
*/
-}( jQuery ) );
+}() );
* TODO: Add a function for restoring the previous text.
* TODO: Accept mappings for converting shortcuts like WP: to Wikipedia:.
*/
-( function ( $, mw ) {
+( function () {
$.highlightText = {
} );
};
-}( jQuery, mediaWiki ) );
+}() );
/**
* @class jQuery.plugin.lengthLimit
*/
-( function ( $, mw ) {
+( function () {
var
eventKeys = [
* @class jQuery
* @mixins jQuery.plugin.lengthLimit
*/
-}( jQuery, mediaWiki ) );
+}() );
/**
* @class jQuery.plugin.localize
*/
-( function ( $, mw ) {
+( function () {
/**
* Gets a localized message, using parameters from options if present.
* @mixins jQuery.plugin.localize
*/
-}( jQuery, mediaWiki ) );
+}() );
*
* @class jQuery.plugin.makeCollapsible
*/
-( function ( $, mw ) {
+( function () {
/**
* Handler for a click on a collapsible toggler.
*
* @mixins jQuery.plugin.makeCollapsible
*/
-}( jQuery, mediaWiki ) );
+}() );
/**
* JavaScript to show jump links to motor-impaired users when they are focused.
*/
-jQuery( function ( $ ) {
+$( function () {
$( '.mw-jump' ).on( 'focus blur', 'a', function ( e ) {
// Confusingly jQuery leaves e.type as focusout for delegated blur events
* input or not.
*/
-( function ( $, mw ) {
+( function () {
var hasOwn = Object.hasOwnProperty;
* @mixins jQuery.plugin.suggestions
*/
-}( jQuery, mediaWiki ) );
+}() );
/**
* @class jQuery.plugin.tabIndex
*/
-( function ( $ ) {
+( function () {
/**
* Find the lowest tabindex in use within a selection.
* @mixins jQuery.plugin.tabIndex
*/
-}( jQuery ) );
+}() );
* $textbox.textSelection( 'encapsulateSelection', { pre: '<b>', post: '</b>' } );
* // Result: Textbox contains 'This is <b>bold</b>!', with cursor before the '!'
*/
-( function ( $ ) {
+( function () {
/**
* Do things to the selection in a `<textarea>`, or a textarea-like editable element.
*
* @inheritdoc jQuery.plugin.textSelection#textSelection
*/
-}( jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* Create an object like mw.Api, but automatically handling everything required to communicate
// Expose
mw.ForeignApi = CoreForeignApi;
-}( mediaWiki, jQuery ) );
+}() );
/* global moment, Uint8Array */
-( function ( $, mw ) {
+( function () {
/**
* mw.ForeignStructuredUpload.BookletLayout encapsulates the process
this.dateWidget.setValue( '' ).setValidityFlag( true );
};
-}( jQuery, mediaWiki ) );
+}() );
-( function ( mw, $, OO ) {
+( function () {
/**
* Used to represent an upload in progress on the frontend.
*
};
mw.ForeignStructuredUpload = ForeignStructuredUpload;
-}( mediaWiki, jQuery, OO ) );
+}() );
-( function ( mw, OO, $ ) {
+( function () {
/**
* Used to represent an upload in progress on the frontend.
*
};
mw.ForeignUpload = ForeignUpload;
-}( mediaWiki, OO, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* @class mw.RegExp
*/
return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
}
};
-}( mediaWiki ) );
+}() );
* @since 1.18
*/
-( function ( mw, $ ) {
+( function () {
/**
* Parse titles into an object structure. Note that when using the constructor
* directly, passing invalid titles will result in an exception. Use #newFromText to use the
// Expose
mw.Title = Title;
-}( mediaWiki, jQuery ) );
+}() );
// (It is excluded in jsduck.json.)
// ESLint suggests unquoting some object keys, which would render the file unparseable by Opera 12.
/* eslint-disable quote-props */
-( function ( mw ) {
+( function () {
var toUpperMapping = {
'ß': 'ß',
'ʼn': 'ʼn',
var mapped = toUpperMapping[ chr ];
return mapped || chr.toUpperCase();
};
-}( mediaWiki ) );
+}() );
/* global moment */
-( function ( $, mw, moment ) {
+( function () {
/**
* mw.Upload.BookletLayout encapsulates the process of uploading a file
this.filenameUsageWidget.setValue( null );
};
-}( jQuery, mediaWiki, moment ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* mw.Upload.Dialog controls a {@link mw.Upload.BookletLayout BookletLayout}.
this.uploadBooklet.clear();
}, this );
};
-}( jQuery, mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var UP;
/**
};
mw.Upload = Upload;
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Uri
*/
-( function ( mw, $ ) {
+( function () {
var parser, properties;
/**
return location.href;
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:RevisionDelete
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var colonSeparator = mw.message( 'colon-separator' ).text(),
summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* Scripts for action=delete at domready
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var colonSeparator = mw.message( 'colon-separator' ).text(),
summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
reason.$input.byteLimit( summaryByteLimit, filterFn );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
var collapsibleLists, handleOne;
// Collapsible lists of categories and templates
);
}
} );
-}( mediaWiki ) );
+}() );
/*
* Javascript for module editWarning
*/
-( function ( mw, $ ) {
+( function () {
'use strict';
$( function () {
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* Scripts for action=edit at domready
*/
-( function ( mw, $ ) {
+( function () {
'use strict';
/**
} );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* Live edit preview.
*/
-( function ( mw, $ ) {
+( function () {
/**
* @ignore
$( document.body ).on( 'click', '#wpPreview, #wpDiff', doLivePreview );
} );
-}( mediaWiki, jQuery ) );
+}() );
* Scripts for pre-emptive edit preparing on action=edit
*/
-( function ( mw, $ ) {
+( function () {
if ( !mw.config.get( 'wgAjaxEditStash' ) ) {
return;
}
checkStash();
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for History action
*/
-jQuery( function ( $ ) {
+$( function () {
var $historyCompareForm = $( '#mw-history-compare' ),
$historySubmitter,
$lis = $( '#pagehistory > li' );
/*!
* Enables double-click-to-edit functionality.
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
mw.util.$content.dblclick( function ( e ) {
var $a;
}
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
*
* See also ImagePage.php#makeMetadataTable (creates the HTML)
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var $tables = $( '.mw_metadata' );
if ( !$tables.find( '.mw-metadata-collapsible, .collapsable' ).length ) {
$tables.addClass( 'collapsed' );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
/**
} );
}
-}( mediaWiki, jQuery ) );
+}() );
* This is loaded in the top queue, so avoid unnecessary dependencies
* like mediawiki.Title or mediawiki.Uri.
*/
-( function ( mw, $ ) {
+( function () {
var profile = $.client.profile(),
canonical = mw.config.get( 'wgInternalRedirectTargetUrl' ),
fragment = null,
} );
}
-}( mediaWiki, jQuery ) );
+}() );
* When the user right-clicks in a heading, it will open the
* edit screen.
*/
-( function ( $ ) {
+( function () {
// Trigger this when a contextmenu click on the page targets an h1-h6 element.
// This uses a delegate handler which 1) starts immediately instead of blocking
// response on dom-ready, and 2) selects and binds once instead of N times.
$edit.get( 0 ).click();
}
} );
-}( jQuery ) );
+}() );
/**
* @class mw.Api.plugin.category
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
/**
* @mixins mw.Api.plugin.category
*/
-}( mediaWiki, jQuery ) );
+}() );
/**
* @class mw.Api.plugin.edit
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
* @mixins mw.Api.plugin.edit
*/
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* @class mw.Api
];
mw.log.deprecate( mw.Api, 'warnings', mw.Api.warnings, null, 'mw.Api.warnings' );
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.login
* @since 1.22
*/
-( function ( mw, $ ) {
+( function () {
'use strict';
$.extend( mw.Api.prototype, {
* @mixins mw.Api.plugin.login
*/
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.messages
* @since 1.27
*/
-( function ( mw, $ ) {
+( function () {
'use strict';
$.extend( mw.Api.prototype, {
* @mixins mw.Api.plugin.messages
*/
-}( mediaWiki, jQuery ) );
+}() );
/**
* @class mw.Api.plugin.options
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
* @mixins mw.Api.plugin.options
*/
-}( mediaWiki, jQuery ) );
+}() );
/**
* @class mw.Api.plugin.parse
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
/**
* @mixins mw.Api.plugin.parse
*/
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.rollback
* @since 1.28
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
/**
* @mixins mw.Api.plugin.rollback
*/
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.upload
* @singleton
*/
-( function ( mw, $ ) {
+( function () {
var nonce = 0,
fieldsAllowed = {
stash: true,
* @class mw.Api
* @mixins mw.Api.plugin.upload
*/
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.user
* @since 1.27
*/
-( function ( mw, $ ) {
+( function () {
$.extend( mw.Api.prototype, {
* @mixins mw.Api.plugin.user
*/
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.Api.plugin.watch
* @since 1.19
*/
-( function ( mw, $ ) {
+( function () {
/**
* @private
* @mixins mw.Api.plugin.watch
*/
-}( mediaWiki, jQuery ) );
+}() );
* Jeroen De Dauw <jeroendedauw at gmail dot com>
*/
-( function ( $ ) {
+( function () {
'use strict';
$( function () {
} );
-}( jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* Prevent the closing of a window with a confirm message (the onbeforeunload event seems to
* work in most browsers.)
}
};
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
/**
}
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
var debug,
debug.init();
} );
-}( mediaWiki, jQuery ) );
+}() );
* @private
* @class jQuery.plugin.footHovzer
*/
-( function ( $ ) {
+( function () {
var $hovzer, footHovzer, $spacer;
function getHovzer() {
* @mixins jQuery.plugin.footHovzer
*/
-}( jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var CONTROL_BUCKET = 'control',
MAX_INT32_UNSIGNED = 4294967295;
}
};
-}( mediaWiki, jQuery ) );
+}() );
* @author Moriel Schottlender, 2015
* @since 1.19
*/
-( function ( mw, $ ) {
+( function () {
/**
* This is a way of getting simple feedback from users. It's useful
* for testing new features -- users can give you feedback without
return this.bugReportLink;
};
-}( mediaWiki, jQuery ) );
+}() );
* @author Mark Holmquist, 2015
* @since 1.25
*/
-( function ( mw, $, oo ) {
+( function () {
var warningConfig = mw.config.get( 'wgFileWarning' ),
warningMessages = warningConfig.messages,
warningLink = warningConfig.link,
.addClass( 'mediawiki-filewarning-info empty' ),
$footer = $( '<p>' )
.addClass( 'mediawiki-filewarning-footer empty' ),
- dialog = new oo.ui.PopupButtonWidget( {
+ dialog = new OO.ui.PopupButtonWidget( {
classes: [ 'mediawiki-filewarning-anchor' ],
label: $mimetype,
flags: [ 'warning' ],
// object at all. Sort of nasty, but it gets the job done.
dialog.getPopup().toggle = $.noop;
}
-}( mediaWiki, jQuery, OO ) );
+}() );
-( function ( mw, $ ) {
+( function () {
// FIXME: mw.htmlform.Element also sets this to empty object
mw.htmlform = {};
return this;
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
// FIXME: mw.htmlform.Checker also sets this to empty object
mw.htmlform = {};
OO.inheritClass( mw.htmlform.ActionFieldLayout, OO.ui.ActionFieldLayout );
OO.mixinClass( mw.htmlform.ActionFieldLayout, mw.htmlform.Element );
-}( mediaWiki ) );
+}() );
* HTMLForm enhancements:
* Set up autocomplete fields.
*/
-( function ( mw, $ ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var $autocomplete = $root.find( '.mw-htmlform-autocomplete' );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Infuse some OOUI HTMLForm fields (those which benefit from always being infused).
*/
-( function ( mw, $ ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var $oouiNodes, modules, extraModules;
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Show fancy tooltips for checkmatrix fields.
*/
-( function ( mw ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var $matrixTooltips = $root.find( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
}
} );
-}( mediaWiki ) );
+}() );
* HTMLForm enhancements:
* Add/remove cloner clones without having to resubmit the form.
*/
-( function ( mw, $ ) {
+( function () {
var cloneCounter = 0;
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Set up 'hide-if' behaviors for form fields that have them.
*/
-( function ( mw, $ ) {
+( function () {
/**
* Helper function for hide-if to find the nearby form field.
}
v = spec[ 2 ];
- if ( !( field instanceof jQuery ) ) {
+ if ( !( field instanceof $ ) ) {
// field is a OO.ui.Widget
if ( field.supports( 'isSelected' ) ) {
getVal = function () {
// It is impossible to submit a form with hidden fields failing validation, e.g. one that
// is required. However, validity is not checked for disabled fields, as these are not
// submitted with the form. So we should also disable fields when hiding them.
- if ( self instanceof jQuery ) {
+ if ( self instanceof $ ) {
// This also finds elements inside any nested fields (in case of HTMLFormFieldCloner),
// which is problematic. But it works because:
// * HTMLFormFieldCloner::createFieldsForKey() copies 'hide-if' rules to nested fields
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
$( function () {
mw.hook( 'htmlform.enhance' ).fire( $( document ) );
$root.find( '.mw-htmlform' ).removeAttr( 'novalidate' );
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Convert multiselect fields from checkboxes to Chosen selector when requested.
*/
-( function ( mw, $ ) {
+( function () {
function addMulti( $oldContainer, $container ) {
var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
}
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Add a dynamic max length to the reason field of SelectAndOther.
*/
-( function ( mw, $ ) {
+( function () {
// cache the separator to avoid object creation on each keypress
var colonSeparator = mw.message( 'colon-separator' ).text();
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* HTMLForm enhancements:
* Animate the SelectOrOther fields, to only show the text field when 'other' is selected.
*/
-( function ( mw, $ ) {
+( function () {
/**
* @class jQuery.plugin.htmlform
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/* eslint-disable no-console */
-( function ( mw, $ ) {
+( function () {
// mw.inspect is a singleton class with static methods
// that itself can also be invoked as a function (mediawiki.base/mw#inspect).
mw.log( 'mw.inspect: reports are not available in debug mode.' );
}
-}( mediaWiki, jQuery ) );
+}() );
* @author neilk@wikimedia.org
* @author mflaschen@wikimedia.org
*/
-( function ( mw, $ ) {
+( function () {
/**
* @class mw.jqueryMsg
* @singleton
if ( typeof children[ i ] !== 'object' ) {
children[ i ] = document.createTextNode( children[ i ] );
}
- if ( children[ i ] instanceof jQuery && children[ i ].hasClass( 'mediaWiki_htmlEmitter' ) ) {
+ if ( children[ i ] instanceof $ && children[ i ].hasClass( 'mediaWiki_htmlEmitter' ) ) {
children[ i ] = children[ i ].contents();
}
}
* @return {string} Textual value of input
*/
function textify( input ) {
- if ( input instanceof jQuery ) {
+ if ( input instanceof $ ) {
input = input.text();
}
return String( input );
var $el,
arg = nodes[ 0 ],
contents = nodes[ 1 ];
- if ( arg instanceof jQuery && !arg.hasClass( 'mediaWiki_htmlEmitter' ) ) {
+ if ( arg instanceof $ && !arg.hasClass( 'mediaWiki_htmlEmitter' ) ) {
$el = arg;
} else {
$el = $( '<a>' );
for ( formIndex = 0; formIndex < forms.length; formIndex++ ) {
form = forms[ formIndex ];
- if ( form instanceof jQuery && form.hasClass( 'mediaWiki_htmlEmitter' ) ) {
+ if ( form instanceof $ && form.hasClass( 'mediaWiki_htmlEmitter' ) ) {
// This is a nested node, may be an explicit plural form like 5=[$2 linktext]
firstChild = form.contents().get( 0 );
if ( firstChild && firstChild.nodeType === Node.TEXT_NODE ) {
};
}() );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
'use strict';
/**
}
};
-}( mediaWiki ) );
+}() );
/*
* Language-fallback-chain-related utilities for mediawiki.language.
*/
-( function ( mw, $ ) {
+( function () {
/**
* @class mw.language
*/
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Base language object with methods related to language support, attempting to mirror some of the
* functionality of the Language class in MediaWiki:
}
};
-}( mediaWiki ) );
+}() );
/*
* Methods for transforming message syntax.
*/
-( function ( mw, $ ) {
+( function () {
/**
* @class mw.language
return forms[ form ][ word ];
}
- transformations = mediaWiki.language.getData( userLanguage, 'grammarTransformations' );
+ transformations = mw.language.getData( userLanguage, 'grammarTransformations' );
if ( !( transformations && transformations[ form ] ) ) {
return word;
}
} );
-}( mediaWiki, jQuery ) );
+}() );
*
* Loading this module also ensures the availability of appropriate messages via mw.msg.
*/
-( function ( mw ) {
+( function () {
var
monthMessages = [
'january', 'february', 'march', 'april',
abbrev: monthAbbrevMessages.map( mwMsgMapper )
};
-}( mediaWiki ) );
+}() );
/*
* Number-related utilities for mediawiki.language.
*/
-( function ( mw, $ ) {
+( function () {
/**
* @class mw.language
*/
} );
-}( mediaWiki, jQuery ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( mw, $ ) {
+( function () {
var ProtectionForm,
reasonCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
reasonByteLimit = mw.config.get( 'wgCommentByteLimit' );
$( ProtectionForm.init.bind( ProtectionForm ) );
-}( mediaWiki, jQuery ) );
+}() );
/**
* MediaWiki legacy wikibits
*/
-( function ( mw, $ ) {
+( function () {
var msg,
loadedScripts = {};
}, 'Use jQuery or mw.loader.load instead.', 'document.' + method );
} );
-}( mediaWiki, jQuery ) );
+}() );
/* global JpegMeta */
-( function ( mw ) {
+( function () {
// Export as module
module.exports = function ( fileReaderResult, fileName ) {
// Back-compat: Also expose via mw.lib
// @deprecated since 1.31
mw.log.deprecate( mw.libs, 'jpegmeta', module.exports );
-}( mediaWiki ) );
+}() );
module.exports = window.pluralRuleParser;
// Back-compat: Also expose via mw.lib
-mediaWiki.libs.pluralRuleParser = window.pluralRuleParser;
+mw.libs.pluralRuleParser = window.pluralRuleParser;
-( function ( mw, $ ) {
+( function () {
/**
* This is an implementation of MessagePoster for wikitext talk pages.
*
mw.messagePoster.factory.register( 'wikitext', WikitextMessagePoster );
mw.messagePoster.WikitextMessagePoster = WikitextMessagePoster;
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* This is the abstract base class for MessagePoster implementations.
*
* something.
*/
mw.messagePoster.MessagePoster.prototype.post = function () {};
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* Factory for MessagePoster objects. This provides a pluggable to way to script the action
* of adding a message to someone's talk page.
mw.messagePoster = {
factory: new MessagePosterFactory()
};
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.plugin.convertmessagebox
* @singleton
*/
-( function ( mw, $ ) {
+( function () {
'use strict';
/**
module.exports = convertmessagebox;
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
var notification,
mw.notification = notification;
-}( mediaWiki, jQuery ) );
+}() );
/**
* @class mw.plugin.notify
*/
-( function ( mw ) {
+( function () {
'use strict';
/**
* @mixins mw.plugin.notify
*/
-}( mediaWiki ) );
+}() );
* - Toggle gallery captions when focused.
* - Dynamically resize images to fill horizontal space.
*/
-( function ( mw, $ ) {
+( function () {
var $galleries,
bound = false,
lastWidth = window.innerWidth,
}
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* mw.GallerySlideshow: Interface controls for the slideshow gallery
*/
-( function ( mw, $, OO ) {
+( function () {
/**
* mw.GallerySlideshow encapsulates the user interface of the slideshow
* galleries. An object is instantiated for each `.mw-gallery-slideshow`
new mw.GallerySlideshow( this );
} );
} );
-}( mediaWiki, jQuery, OO ) );
+}() );
* Implement AJAX navigation for multi-page images so the user may browse without a full page reload.
*/
-( function ( mw, $ ) {
+( function () {
var jqXhr, $multipageimage, $spinner,
cache = {},
cacheOrder = [];
} );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
* @since 1.21
* @author Marius Hoch <hoo@online.de>
*/
-( function ( mw, $ ) {
+( function () {
if ( !mw.user.tokens.exists( 'patrolToken' ) ) {
// Current user has no patrol right, or an old cached version of user.tokens
// that didn't have patrolToken yet.
e.preventDefault();
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
mw.hook( 'wikipage.content' ).add( function ( $content ) {
var $sortable, $collapsible;
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* @since 1.28
* @author Timo Tijhof
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
$( '.mw-rollback-link' ).on( 'click', 'a[data-mw="interface"]', function ( e ) {
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
// Break out of framesets
if ( mw.config.get( 'wgBreakFrames' ) ) {
// Note: In IE < 9 strict comparison to window is non-standard (the standard didn't exist yet)
}
} );
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.plugin.page.watch.ajax
* @singleton
*/
-( function ( mw, $ ) {
+( function () {
var watch,
// The name of the page to watch or unwatch
title = mw.config.get( 'wgRelevantPageName' );
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* View model for the changes list
*
mw.rcfilters.dm.ChangesListViewModel.prototype.hasUnseenWatchedChanges = function () {
return this.unseenWatchedChanges;
};
-}( mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( mw ) {
+( function () {
/**
* View model for a filter group
*
itemModel.toggleVisible( visibleItems.indexOf( itemModel ) !== -1 );
} );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Filter item model
*
return this.visible;
};
-}( mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( mw, $ ) {
+( function () {
/**
* View model for the filters selection and display
*
this.getItemByName( filterName ).clearHighlightColor();
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* RCFilter base item model
*
mw.rcfilters.dm.ItemModel.prototype.isHighlighted = function () {
return !!this.getHighlightColor();
};
-}( mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( mw, $ ) {
+( function () {
/**
* View model for saved queries
*
mw.rcfilters.dm.SavedQueriesModel.prototype.isConverted = function () {
return this.converted;
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* View model for a single saved query
*
this.emit( 'update' );
}
};
-}( mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( mw, $ ) {
+( function () {
var byteLength = require( 'mediawiki.String' ).byteLength;
this.filtersModel.getViewTrigger( view )
);
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Supported highlight colors.
* Warning: These are also hardcoded in "styles/mw.rcfilters.variables.less"
* @property {string[]}
*/
mw.rcfilters.HighlightColors = [ 'c1', 'c2', 'c3', 'c4', 'c5' ];
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/* eslint no-underscore-dangle: "off" */
/**
* URI Processor for RCFilters
{ urlversion: '2' }
);
};
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:RecentChanges
*/
-( function ( mw, $ ) {
+( function () {
var rcfilters = {
/**
* @member mw.rcfilters
module.exports = rcfilters;
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* @class
* @singleton
}
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget defining the button controlling the popup for the number of results
*
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget defining the popup to choose number of results
*
mw.rcfilters.ui.ChangesLimitPopupWidget.prototype.onGroupByPageModelUpdate = function () {
this.groupByPageCheckbox.setSelected( this.groupByPageItemModel.isSelected() );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* List of changes
*
// Turn off highlights
this.$element.removeClass( 'mw-rcfilters-ui-changesListWrapperWidget-highlighted' );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A widget representing a single toggle filter
*
mw.rcfilters.ui.CheckboxInputWidget.prototype.onUserChange = function () {
this.emit( 'userChange', this.$input.prop( 'checked' ) );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget defining the popup to choose date for the results
*
*
* A days item was chosen
*/
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* A button to configure highlight for a filter item
*
);
} );
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* Menu header for the RCFilters filters menu
*
mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onInvertNamespacesButtonClick = function () {
this.controller.toggleInvertedNamespaces();
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A widget representing a single toggle filter
*
} );
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A widget representing a menu section for filter groups
*
return this.model.getName();
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Extend OOUI's FilterTagItemWidget to also display a popup on hover.
*
invalid: this.itemModel.isSelected() && this.itemModel.isConflicted()
} );
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* List displaying all filter groups
*
} );
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* List displaying all filter groups
*
mw.rcfilters.ui.FilterWrapperWidget.prototype.onNewChangesExist = function ( newChangesExist ) {
this.showNewChangesLink.toggle( newChangesExist );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Wrapper for the RC form with hide/show links
* Must be constructed after the model is initialized.
this.$element.detach();
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A group widget to allow for aggregation of events
*
OO.inheritClass( mw.rcfilters.ui.GroupWidget, OO.ui.Widget );
OO.mixinClass( mw.rcfilters.ui.GroupWidget, OO.ui.mixin.GroupWidget );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* A widget representing a filter item highlight color picker
*
}
this.emit( 'chooseColor', color );
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* A popup containing a color picker, for setting highlight colors.
*
this.toggle( false );
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A widget representing a base toggle item
*
return this.itemModel;
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget for toggling live updates
*
this.setState( enable );
};
-}( mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* Wrapper for changes list content
*
return new mw.rcfilters.ui.FormWrapperWidget(
this.model, this.changesListModel, this.controller, this.$formContainer );
};
-}( jQuery, mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Button for marking all changes as seen on the Watchlist
*
this.setDisabled( !this.model.hasUnseenWatchedChanges() );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A floating menu widget for the filter list
*
mw.rcfilters.ui.MenuSelectWidget.prototype.setUserSelecting = function ( isSelecting ) {
this.userSelecting = !!isSelecting;
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Top section (between page title and filters) on Special:Recentchanges
*
}
this.$topLinks.toggleClass( 'mw-recentchanges-toplinks-collapsed', state === 'collapsed' );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget to select and display target page on Special:RecentChangesLinked (AKA Related Changes)
*
this.titleSearch.setValue( text );
this.titleSearch.setTitle( text );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget to select to view changes that link TO or FROM the target page
* on Special:RecentChangesLinked (AKA Related Changes)
'rcfilters-filter-showlinkedfrom-label'
) );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Top section (between page title and filters) on Special:RecentChangesLinked (AKA RelatedChanges)
*
/* Initialization */
OO.inheritClass( mw.rcfilters.ui.RclTopSectionWidget, OO.ui.Widget );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Save filters widget. This widget is displayed in the tag area
* and allows the user to save the current state of the system
this.emit( 'saveCurrent' );
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Quick links menu option widget
*
return this.model.getID();
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Quick links widget
*
this.menu.removeItems( [ this.menu.findItemFromData( item.getID() ) ] );
this.placeholderItem.toggle( this.model.isEmpty() );
};
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/**
* Extend OOUI's TagItemWidget to also display a popup on hover.
*
this.itemModel.disconnect( this );
this.closeButton.disconnect( this );
};
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Widget defining the behavior used to choose from a set of values
* in a single_value group
this.selectWidget.selectItemByData( selectedItem.getName() );
}
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* A widget for the footer for the default view, allowing to switch views
*
mw.rcfilters.ui.ViewSwitchWidget.prototype.onButtonClick = function ( buttonWidget ) {
this.controller.switchView( buttonWidget.getData() );
};
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
/**
* Top section (between page title and filters) on Special:Watchlist
*
/* Initialization */
OO.inheritClass( mw.rcfilters.ui.WatchlistTopSectionWidget, OO.ui.Widget );
-}( mediaWiki ) );
+}() );
/*!
* Add search suggestions to the search form.
*/
-( function ( mw, $ ) {
+( function () {
var searchNS = $.map( mw.config.get( 'wgFormattedNamespaces' ), function ( nsName, nsID ) {
if ( nsID >= 0 && mw.user.options.get( 'searchNs' + nsID ) ) {
// Cast string key to number
.find( '.mw-fallbackSearchButton' ).remove();
} );
-}( mediaWiki, jQuery ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( $, mw, OO ) {
+( function () {
'use strict';
var ApiSandbox, Util, WidgetMethods, Validators,
$content, panel, booklet, oldhash, windowManager,
module.exports = ApiSandbox;
-}( jQuery, mediaWiki, OO ) );
+}() );
/*!
* JavaScript for Special:Block
*/
-( function ( mw, $ ) {
+( function () {
// Like OO.ui.infuse(), but if the element doesn't exist, return null instead of throwing an exception.
function infuseOrNull( elem ) {
try {
updateBlockOptions();
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for change credentials form.
*/
-( function ( mw, $, OO ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var api = new mw.Api();
} );
} );
} );
-}( mediaWiki, jQuery, OO ) );
+}() );
*/
/* Remember the collapse state of the legend on recent changes and watchlist pages. */
-( function ( mw ) {
+( function () {
var
cookieName = 'changeslist-state',
// Expanded by default
};
mw.hook( 'wikipage.content' ).add( doCollapsibleLegend );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
// Return a promise that is resolved when the element is blurred (loses focus).
// If it already is blurred, resolved immediately.
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:EditTags
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Import
*/
-( function ( $ ) {
+( function () {
var subprojectListAlreadyShown;
function updateImportSubprojectList() {
var $projectField = $( '#mw-import-table-interwiki #interwiki' ),
updateImportSubprojectList();
}
} );
-}( jQuery ) );
+}() );
/*!
* JavaScript for Special:MovePage
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
mw.widgets.visibleByteLimit( wpReason, summaryByteLimit );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript module used on Special:PageLanguage
*/
-( function ( $, OO ) {
+( function () {
$( function () {
// Select the 'Language select' option if user is trying to select language
OO.ui.infuse( 'mw-pl-languageselector' ).on( 'change', function () {
OO.ui.infuse( 'mw-pl-options' ).setValue( '2' );
} );
} );
-}( jQuery, OO ) );
+}() );
/*!
* JavaScript for Special:Preferences: editfont field enhancements.
*/
-( function ( mw ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var widget, lastValue,
$target = $root.find( '#mw-input-wpeditfont' );
updateLabel( widget.getValue() );
} );
-}( mediaWiki ) );
+}() );
/*!
* JavaScript for Special:Preferences: Tab navigation.
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var $preferences, tabs, wrapper, previousTab, switchingNoHash;
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* JavaScript for Special:Preferences: Enable save button and prevent the window being accidentally
* closed when any form field is changed.
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var allowCloseWindow, saveButton, restoreButton,
oouiEnabled = $( '#mw-prefs-form' ).hasClass( 'mw-htmlform-ooui' );
$( '#mw-prefs-restoreprefs' ).on( 'click', allowCloseWindow.release );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Preferences: Check for successbox to replace with notifications.
*/
-( function ( $ ) {
+( function () {
$( function () {
var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' );
convertmessagebox();
} );
-}( jQuery ) );
+}() );
/*!
* JavaScript for Special:Preferences: Email preferences better UX
*/
-( function ( $ ) {
+( function () {
$( function () {
var allowEmail, allowEmailFromNewUsers;
toggleDisabled();
}
} );
-}( jQuery ) );
+}() );
/*!
* JavaScript for Special:Preferences: Tab navigation.
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var $preftoc, $preferences, $fieldsets, labelFunc, previousTab;
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Preferences: Timezone field enhancements.
*/
-( function ( mw, $ ) {
+( function () {
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var $tzSelect, $tzTextbox, timezoneWidget, $localtimeHolder, servertime,
$target = $root.find( '#wpTimeCorrection' ),
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:RecentChanges
*/
-( function ( mw, $ ) {
+( function () {
var rc, $checkboxes, $select;
/**
module.exports = rc;
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:RevisionDelete
*/
-( function ( mw, $ ) {
+( function () {
var colonSeparator = mw.message( 'colon-separator' ).text(),
summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
$wpReason.byteLimit( summaryByteLimit, filterFn );
}
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var api = new mw.Api(),
pageUrl = new mw.Uri(),
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Search
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var $checkboxes, $headerLinks, updateHeaderLinks, searchWidget;
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Undelete
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
mw.widgets.visibleByteLimit( wpComment, summaryByteLimit );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:UnwatchedPages
*/
-( function ( mw, $ ) {
+( function () {
$( function () {
$( 'a.mw-watch-link' ).click( function ( e ) {
var promise,
e.preventDefault();
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/* global Uint8Array */
-( function ( mw, $ ) {
+( function () {
var uploadWarning, uploadTemplatePreview,
ajaxUploadDestCheck = mw.config.get( 'wgAjaxUploadDestCheck' ),
$license = $( '#wpLicense' );
$( '.mw-editTools' ).attr( 'tabindex', '0' );
setEditTabindex( '-1' );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for signup form.
*/
-( function ( mw, $ ) {
+( function () {
// When sending password by email, hide the password input fields.
$( function () {
// Always required if checked, otherwise it depends, so we use the original
passwordChecker = new mw.htmlform.Checker( $passwordInput, checkPassword );
passwordChecker.attach( $usernameInput.add( $emailInput ).add( $realNameInput ) );
} );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:UserRights
*/
-( function ( mw, $ ) {
+( function () {
var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' ),
summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
$wpReason.byteLimit( summaryByteLimit );
}
-}( mediaWiki, jQuery ) );
+}() );
/*!
* JavaScript for Special:Watchlist
*/
-( function ( $ ) {
+( function () {
$( function () {
$( '.mw-changeslist-line-watched .mw-title a' ).on( 'click', function () {
$( this )
.removeClass( 'mw-changeslist-line-watched' );
} );
} );
-}( jQuery ) );
+}() );
/*!
* JavaScript for Special:Watchlist
*/
-( function ( mw, $, OO ) {
+( function () {
$( function () {
var api = new mw.Api(), $progressBar, $resetForm = $( '#mw-watchlist-resetbutton' );
}
} );
-}( mediaWiki, jQuery, OO )
-);
+}() );
-( function ( mw ) {
+( function () {
'use strict';
// Catch exceptions to avoid fatal in Chrome's "Block data storage" mode
*/
mw.storage.session = new SafeStorage( sessionStorage );
-}( mediaWiki ) );
+}() );
* @class mw.template
* @singleton
*/
-( function ( mw, $ ) {
+( function () {
var compiledTemplates = {},
compilers = {};
}
} );
-}( mediaWiki, jQuery ) );
+}() );
/* global Mustache */
-( function ( mw, $ ) {
+( function () {
// Register mustache compiler
mw.template.registerCompiler( 'mustache', {
compile: function ( src ) {
}
} );
-}( mediaWiki, jQuery ) );
+}() );
-mediaWiki.template.registerCompiler( 'regexp', {
+mw.template.registerCompiler( 'regexp', {
compile: function ( src ) {
return {
render: function () {
-( function ( mw, $ ) {
+( function () {
'use strict';
// Table of contents toggle
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* @class mw.toolbar
* @singleton
*/
-( function ( mw, $ ) {
+( function () {
var toolbar, isReady, $toolbar, queue, slice, $currentFocused;
/**
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
* @singleton
*/
/* global Uint16Array */
-( function ( mw, $ ) {
+( function () {
var userInfoPromise, pageviewRandomId;
/**
*/
mw.log.deprecate( mw.user, 'stickyRandomId', mw.user.getPageviewToken, 'Please use getPageviewToken instead' );
-}( mediaWiki, jQuery ) );
+}() );
/*!
* Add autocomplete suggestions for names of registered users.
*/
-( function ( mw, $ ) {
+( function () {
var api, config;
config = {
$( function () {
$( '.mw-autocomplete-user' ).suggestions( config );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
var util;
mw.util = util;
module.exports = util;
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
/**
};
mw.viewport = viewport;
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, document ) {
+( function () {
var hidden, visibilityChange,
nextVisibleTimeoutId = 0,
activeTimeouts = {},
+ document = window.document,
init = function ( overrideDoc ) {
if ( overrideDoc !== undefined ) {
document = overrideDoc;
module.exports.setDocument = init;
}
-}( mediaWiki, document ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* CalendarWidget displays a calendar that can be used to select a date. It
return this;
};
-}( jQuery, mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( $, mw ) {
+( function () {
/**
* Provides various methods needed for formatting dates and times.
};
};
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* DateTimeInputWidgets can be used to input a date, a time, or a date and
this.focus();
};
-}( jQuery, mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( $, mw ) {
+( function () {
/**
* Provides various methods needed for formatting dates and times. This
return ret;
};
-}( jQuery, mediaWiki ) );
+}() );
/* eslint-disable no-restricted-properties */
-( function ( $, mw ) {
+( function () {
/**
* Provides various methods needed for formatting dates and times. This
return ret;
};
-}( jQuery, mediaWiki ) );
+}() );
// Create the namespace object
-mediaWiki.widgets.datetime = {};
+mw.widgets.datetime = {};
-( function ( mw ) {
+( function () {
var byteLength = require( 'mediawiki.String' ).byteLength,
codePointLength = require( 'mediawiki.String' ).codePointLength;
textInputWidget.$input.codePointLimit( limit );
};
-}( mediaWiki ) );
+}() );
*
* @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org
*/
-( function ( $, mw ) {
+( function () {
/**
* API Results Provider object.
mw.widgets.APIResultsProvider.prototype.setAjaxSettings = function ( settings ) {
this.ajaxSettings = settings;
};
-}( jQuery, mediaWiki ) );
+}() );
*
* @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org
*/
-( function ( $, mw ) {
+( function () {
/**
* API Results Queue object.
mw.widgets.APIResultsQueue.prototype.getThreshold = function () {
return this.threshold;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* MediaWiki media resource provider.
this.apiurl !== undefined ||
this.scriptDirUrl !== undefined;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* MediaWiki media resource queue.
mw.widgets.MediaResourceQueue.prototype.getMaxHeight = function () {
return this.maxHeight;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Creates an mw.widgets.MediaResultWidget object.
mw.widgets.MediaResultWidget.prototype.hasSrc = function () {
return !!this.src;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* MediaWiki media search provider.
mw.widgets.MediaSearchProvider.prototype.isValid = function () {
return this.getUserParams().gsrsearch && mw.widgets.MediaSearchProvider.super.prototype.isValid.call( this );
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( mw ) {
+( function () {
/**
* MediaWiki media resource queue.
mw.widgets.MediaSearchQueue.prototype.getSearchQuery = function () {
return this.getParams().gsrsearch;
};
-}( mediaWiki ) );
+}() );
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Creates an mw.widgets.MediaSearchWidget object.
mw.widgets.MediaSearchWidget.prototype.getLang = function () {
return this.lang;
};
-}( jQuery, mediaWiki ) );
+}() );
* @license The MIT License (MIT); see LICENSE.txt
*/
/* global moment */
-( function ( $, mw ) {
+( function () {
/**
* Creates an mw.widgets.CalendarWidget object.
return this;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
var hasOwn = Object.prototype.hasOwnProperty,
NS_CATEGORY = mw.config.get( 'wgNamespaceIds' ).category;
/** Search only parent categories */
ParentCategories: 4
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
var hasOwn = Object.prototype.hasOwnProperty;
// For backwards compatibility. See T183299.
mw.widgets.CategoryCapsuleItemWidget = mw.widgets.CategoryTagItemWidget;
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* A JavaScript version of CheckMatrixWidget.
*
} );
}
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Namespace input widget. Displays a dropdown box with the choice of available namespaces, plus
return this;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Like TitleInputWidget, but the namespace has to be input through a separate dropdown field.
return this;
};
-}( jQuery, mediaWiki ) );
+}() );
* @license The MIT License (MIT); see LICENSE.txt
*/
/* global moment */
-( function ( $, mw ) {
+( function () {
/**
* Creates an mw.widgets.DateInputWidget object.
}
};
-}( jQuery, mediaWiki ) );
+}() );
* @license The MIT License (MIT); see LICENSE.txt
*/
/* global moment */
-( function ( $, mw ) {
+( function () {
/**
* Creates a mw.widgets.ExpiryWidget object.
return this.relativeField.getValue();
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Namespace input widget. Displays a dropdown box with the choice of available namespaces.
return options;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Creates a mw.widgets.SearchInputWidget object.
return items;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2017 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Select with input widget. Displays an OO.ui.TextInputWidget along with
this.emit( 'change', this.getValue() );
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2018 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* RadioSelectInputWidget and a TextInputWidget to set minimum or maximum byte size
this.textinput.restorePreInfuseState( state.textinput );
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2016 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw, OO ) {
+( function () {
/**
* Accepts a stashed file and displays the information for purposes of
return $.Deferred().reject( 'No filekey' );
};
-}( jQuery, mediaWiki, OO ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
var trimByteLength = require( 'mediawiki.String' ).trimByteLength;
} ).newVal;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Creates a mw.widgets.TitleOptionWidget object.
OO.inheritClass( mw.widgets.TitleOptionWidget, OO.ui.MenuOptionWidget );
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( mw ) {
+( function () {
/**
* Creates an mw.widgets.TitleSearchWidget object.
return response.query || {};
};
-}( mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
var hasOwn = Object.prototype.hasOwnProperty;
/**
return !!this.getMWTitle();
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* Creates a mw.widgets.UserInputWidget object.
return items;
};
-}( jQuery, mediaWiki ) );
+}() );
* @copyright 2017 MediaWiki Widgets Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
-( function ( $, mw ) {
+( function () {
/**
* UsersMultiselectWidget can be used to input list of users in a single
this.input.setValue( '' );
};
-}( jQuery, mediaWiki ) );
+}() );
-/* global mediaWiki, moment */
+/* global moment */
-( function ( mw ) {
+( function () {
// HACK: Overwrite moment's i18n with MediaWiki's for the current language so that
// wgTranslateNumerals is respected.
moment.updateLocale( moment.locale(), {
return s;
}
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
var isMobile;
// Connect OOUI to MediaWiki's localisation system
OO.ui.getUserLanguages = mw.language.getFallbackLanguageChain;
}
return isMobile;
};
-}( mediaWiki ) );
+}() );
use CommentStoreComment;
use Content;
+use ContentHandler;
use LinksUpdate;
use MediaWiki\MediaWikiServices;
use MediaWiki\Storage\DerivedPageDataUpdater;
use MediaWiki\Storage\RevisionSlotsUpdate;
use MediaWiki\Storage\SlotRecord;
use MediaWikiTestCase;
+use MWCallableUpdate;
+use PHPUnit\Framework\MockObject\MockObject;
+use TextContent;
+use TextContentHandler;
use Title;
use User;
use Wikimedia\TestingAccessWrapper;
$dataUpdates = $updater->getSecondaryDataUpdates();
- // TODO: MCR: assert updates from all slots!
$this->assertNotEmpty( $dataUpdates );
$linksUpdates = array_filter( $dataUpdates, function ( $du ) {
$this->assertCount( 1, $linksUpdates );
}
+ /**
+ * @param string $name
+ *
+ * @return ContentHandler
+ */
+ private function defineMockContentModelForUpdateTesting( $name ) {
+ /** @var ContentHandler|MockObject $handler */
+ $handler = $this->getMockBuilder( TextContentHandler::class )
+ ->setConstructorArgs( [ $name ] )
+ ->setMethods(
+ [ 'getSecondaryDataUpdates', 'getDeletionUpdates', 'unserializeContent' ]
+ )
+ ->getMock();
+
+ $dataUpdate = new MWCallableUpdate( 'time' );
+ $dataUpdate->_name = "$name data update";
+
+ $deletionUpdate = new MWCallableUpdate( 'time' );
+ $deletionUpdate->_name = "$name deletion update";
+
+ $handler->method( 'getSecondaryDataUpdates' )->willReturn( [ $dataUpdate ] );
+ $handler->method( 'getDeletionUpdates' )->willReturn( [ $deletionUpdate ] );
+ $handler->method( 'unserializeContent' )->willReturnCallback(
+ function ( $text ) use ( $handler ) {
+ return $this->createMockContent( $handler, $text );
+ }
+ );
+
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers', [
+ $name => function () use ( $handler ){
+ return $handler;
+ }
+ ]
+ );
+
+ return $handler;
+ }
+
+ /**
+ * @param ContentHandler $handler
+ * @param string $text
+ *
+ * @return Content
+ */
+ private function createMockContent( ContentHandler $handler, $text ) {
+ /** @var Content|MockObject $content */
+ $content = $this->getMockBuilder( TextContent::class )
+ ->setConstructorArgs( [ $text ] )
+ ->setMethods( [ 'getModel', 'getContentHandler' ] )
+ ->getMock();
+
+ $content->method( 'getModel' )->willReturn( $handler->getModelID() );
+ $content->method( 'getContentHandler' )->willReturn( $handler );
+
+ return $content;
+ }
+
+ public function testGetSecondaryDataUpdatesWithSlotRemoval() {
+ global $wgMultiContentRevisionSchemaMigrationStage;
+
+ if ( ! ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ) {
+ $this->markTestSkipped( 'Slot removal cannot happen with MCR being enabled' );
+ }
+
+ $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
+ $a1 = $this->defineMockContentModelForUpdateTesting( 'A1' );
+ $m2 = $this->defineMockContentModelForUpdateTesting( 'M2' );
+
+ $mainContent1 = $this->createMockContent( $m1, 'main 1' );
+ $auxContent1 = $this->createMockContent( $a1, 'aux 1' );
+ $mainContent2 = $this->createMockContent( $m2, 'main 2' );
+
+ $user = $this->getTestUser()->getUser();
+ $page = $this->getPage( __METHOD__ );
+ $this->createRevision(
+ $page,
+ __METHOD__,
+ [ 'main' => $mainContent1, 'aux' => $auxContent1 ]
+ );
+
+ $update = new RevisionSlotsUpdate();
+ $update->modifyContent( 'main', $mainContent2 );
+ $update->removeSlot( 'aux' );
+
+ $page = $this->getPage( __METHOD__ );
+ $updater = $this->getDerivedPageDataUpdater( $page );
+ $updater->prepareContent( $user, $update, false );
+
+ $dataUpdates = $updater->getSecondaryDataUpdates();
+
+ $this->assertNotEmpty( $dataUpdates );
+
+ $updateNames = array_map( function ( $du ) {
+ return isset( $du->_name ) ? $du->_name : get_class( $du );
+ }, $dataUpdates );
+
+ $this->assertContains( LinksUpdate::class, $updateNames );
+ $this->assertContains( 'A1 deletion update', $updateNames );
+ $this->assertContains( 'M2 data update', $updateNames );
+ $this->assertNotContains( 'M1 data update', $updateNames );
+ }
+
/**
* Creates a dummy revision object without touching the database.
*
<?php
use MediaWiki\MediaWikiServices;
+use MediaWiki\Revision\SlotRenderingProvider;
/**
* @group ContentHandler
$this->assertArrayHasKey( 'file_text', $data );
$this->assertEquals( 'This is file content', $data['file_text'] );
}
+
+ public function testGetSecondaryDataUpdates() {
+ $title = Title::newFromText( 'Somefile.jpg', NS_FILE );
+ $content = new WikitextContent( '' );
+
+ /** @var SlotRenderingProvider $srp */
+ $srp = $this->getMock( SlotRenderingProvider::class );
+
+ $handler = new WikitextContentHandler();
+ $updates = $handler->getSecondaryDataUpdates( $title, $content, 'main', $srp );
+
+ $this->assertEquals( [], $updates );
+ }
+
+ public function testGetDeletionUpdates() {
+ $title = Title::newFromText( 'Somefile.jpg', NS_FILE );
+ $content = new WikitextContent( '' );
+
+ $srp = $this->getMock( SlotRenderingProvider::class );
+
+ $handler = new WikitextContentHandler();
+ $updates = $handler->getDeletionUpdates( $title, 'main' );
+
+ $this->assertEquals( [], $updates );
+ }
+
}
use MediaWiki\Edit\PreparedEdit;
use MediaWiki\MediaWikiServices;
use MediaWiki\Storage\RevisionSlotsUpdate;
+use PHPUnit\Framework\MockObject\MockObject;
use Wikimedia\TestingAccessWrapper;
/**
/**
* @param string|Title|WikiPage $page
- * @param string $text
+ * @param string|Content|Content[] $content
* @param int|null $model
*
* @return WikiPage
*/
- protected function createPage( $page, $text, $model = null, $user = null ) {
+ protected function createPage( $page, $content, $model = null, $user = null ) {
if ( is_string( $page ) || $page instanceof Title ) {
$page = $this->newPage( $page, $model );
}
- $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
- $page->doEditContent( $content, "testing", EDIT_NEW, false, $user );
+ if ( !$user ) {
+ $user = $this->getTestUser()->getUser();
+ }
+
+ if ( is_string( $content ) ) {
+ $content = ContentHandler::makeContent( $content, $page->getTitle(), $model );
+ }
+
+ if ( !is_array( $content ) ) {
+ $content = [ 'main' => $content ];
+ }
+
+ $updater = $page->newPageUpdater( $user );
+
+ foreach ( $content as $role => $cnt ) {
+ $updater->setContent( $role, $cnt );
+ }
+
+ $updater->saveRevision( CommentStoreComment::newUnsavedComment( "testing" ) );
return $page;
}
* @covers WikiPage::doDeleteUpdates
*/
public function testDoDeleteUpdates() {
+ $user = $this->getTestUser()->getUser();
$page = $this->createPage(
__METHOD__,
"[[original text]] foo",
CONTENT_MODEL_WIKITEXT
);
$id = $page->getId();
+ $page->loadPageData(); // make sure the current revision is cached.
// Similar to MovePage logic
wfGetDB( DB_MASTER )->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
- $page->doDeleteUpdates( $id );
+ $page->doDeleteUpdates( $page->getId(), $page->getContent(), $page->getRevision(), $user );
// Run the job queue
JobQueueGroup::destroySingletons();
$this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
}
+ /**
+ * @param string $name
+ *
+ * @return ContentHandler
+ */
+ protected function defineMockContentModelForUpdateTesting( $name ) {
+ /** @var ContentHandler|MockObject $handler */
+ $handler = $this->getMockBuilder( TextContentHandler::class )
+ ->setConstructorArgs( [ $name ] )
+ ->setMethods(
+ [ 'getSecondaryDataUpdates', 'getDeletionUpdates', 'unserializeContent' ]
+ )
+ ->getMock();
+
+ $dataUpdate = new MWCallableUpdate( 'time' );
+ $dataUpdate->_name = "$name data update";
+
+ $deletionUpdate = new MWCallableUpdate( 'time' );
+ $deletionUpdate->_name = "$name deletion update";
+
+ $handler->method( 'getSecondaryDataUpdates' )->willReturn( [ $dataUpdate ] );
+ $handler->method( 'getDeletionUpdates' )->willReturn( [ $deletionUpdate ] );
+ $handler->method( 'unserializeContent' )->willReturnCallback(
+ function ( $text ) use ( $handler ) {
+ return $this->createMockContent( $handler, $text );
+ }
+ );
+
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers', [
+ $name => function () use ( $handler ){
+ return $handler;
+ }
+ ]
+ );
+
+ return $handler;
+ }
+
+ /**
+ * @param ContentHandler $handler
+ * @param string $text
+ *
+ * @return Content
+ */
+ protected function createMockContent( ContentHandler $handler, $text ) {
+ /** @var Content|MockObject $content */
+ $content = $this->getMockBuilder( TextContent::class )
+ ->setConstructorArgs( [ $text ] )
+ ->setMethods( [ 'getModel', 'getContentHandler' ] )
+ ->getMock();
+
+ $content->method( 'getModel' )->willReturn( $handler->getModelID() );
+ $content->method( 'getContentHandler' )->willReturn( $handler );
+
+ return $content;
+ }
+
+ public function testGetDeletionUpdates() {
+ $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
+
+ $mainContent1 = $this->createMockContent( $m1, 'main 1' );
+
+ $page = new WikiPage( Title::newFromText( __METHOD__ ) );
+ $page = $this->createPage(
+ $page,
+ [ 'main' => $mainContent1 ]
+ );
+
+ $dataUpdates = $page->getDeletionUpdates( $page->getRevisionRecord() );
+ $this->assertNotEmpty( $dataUpdates );
+
+ $updateNames = array_map( function ( $du ) {
+ return isset( $du->_name ) ? $du->_name : get_class( $du );
+ }, $dataUpdates );
+
+ $this->assertContains( LinksDeletionUpdate::class, $updateNames );
+ $this->assertContains( 'M1 deletion update', $updateNames );
+ }
+
/**
* @covers WikiPage::getRevision
*/
return true;
}
+ public function testGetDeletionUpdates() {
+ $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
+ $a1 = $this->defineMockContentModelForUpdateTesting( 'A1' );
+
+ $mainContent1 = $this->createMockContent( $m1, 'main 1' );
+ $auxContent1 = $this->createMockContent( $a1, 'aux 1' );
+
+ $page = new WikiPage( Title::newFromText( __METHOD__ ) );
+ $page = $this->createPage(
+ $page,
+ [ 'main' => $mainContent1, 'aux' => $auxContent1 ]
+ );
+
+ $dataUpdates = $page->getDeletionUpdates( $page->getRevisionRecord() );
+ $this->assertNotEmpty( $dataUpdates );
+
+ $updateNames = array_map( function ( $du ) {
+ return isset( $du->_name ) ? $du->_name : get_class( $du );
+ }, $dataUpdates );
+
+ $this->assertContains( LinksDeletionUpdate::class, $updateNames );
+ $this->assertContains( 'M1 deletion update', $updateNames );
+ $this->assertContains( 'A1 deletion update', $updateNames );
+ }
+
}
return false;
}
+ public function testGetDeletionUpdates() {
+ $mainContent1 = new WikitextContent( '' );
+
+ $title = Title::makeTitle( $this->getDefaultWikitextNS(), __METHOD__ );
+ $page = new WikiPage( $title );
+ $page = $this->createPage(
+ $page,
+ [ 'main' => $mainContent1 ]
+ );
+
+ $dataUpdates = $page->getDeletionUpdates( $page->getRevisionRecord() );
+ $this->assertNotEmpty( $dataUpdates );
+
+ $updateNames = array_map( function ( $du ) {
+ return isset( $du->_name ) ? $du->_name : get_class( $du );
+ }, $dataUpdates );
+
+ $this->assertContains( LinksDeletionUpdate::class, $updateNames );
+ }
+
}
-mediaWiki.loader.testCallback();
+mw.loader.testCallback();
/* global sinon */
-( function ( $, mw, QUnit ) {
+( function () {
'use strict';
var addons, nested;
} );
} );
-}( jQuery, mediaWiki, QUnit ) );
+}() );
-( function ( $ ) {
+( function () {
var getAccessKeyPrefixTestData, updateTooltipAccessKeysTestData;
QUnit.module( 'jquery.accessKeyLabel', QUnit.newMwEnvironment( {
$.fn.updateTooltipAccessKeys.setTestMode( false );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.color', QUnit.newMwEnvironment() );
QUnit.test( 'animate', function ( assert ) {
} )
.always( done );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.colorUtil', QUnit.newMwEnvironment() );
QUnit.test( 'getRGB', function ( assert ) {
b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
assert.strictEqual( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
QUnit.test( 'getAttrs()', function ( assert ) {
assert.propEqual( $el.getAttrs(), attrs, 'keys and values match' );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.hidpi', QUnit.newMwEnvironment() );
QUnit.test( 'devicePixelRatio', function ( assert ) {
assert.strictEqual( $.matchSrcSet( 1.75, srcset ), 'onefive.png', '1.75 gives match to 1.5' );
assert.strictEqual( $.matchSrcSet( 2.25, srcset ), 'two.png', '2.25 gives match to 2' );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.highlightText', QUnit.newMwEnvironment() );
QUnit.test( 'Check', function ( assert ) {
);
} );
} );
-}( jQuery ) );
+}() );
-( function ( $, mw ) {
+( function () {
var simpleSample, U_20AC, poop, mbSample;
QUnit.module( 'jquery.lengthLimit', QUnit.newMwEnvironment() );
expected: '\uD800'
} );
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
QUnit.module( 'jquery.localize', QUnit.newMwEnvironment() );
QUnit.test( 'Handle basic replacements', function ( assert ) {
assert.strictEqual( $lc.length, 1, 'link is created' );
assert.strictEqual( $lc.text(), 'link', 'the link text got added' );
} );
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $ ) {
+( function () {
var loremIpsum = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
QUnit.module( 'jquery.makeCollapsible', QUnit.newMwEnvironment() );
assert.assertFalse( $collapsible2.find( '> .mw-collapsible-toggle' ).hasClass( 'mw-collapsible-toggle-collapsed' ) );
} ).find( '> .mw-collapsible-toggle a' ).trigger( 'click' );
} );
-}( jQuery ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
QUnit.test( 'firstTabIndex', function ( assert ) {
$testB = $( '<div>' );
assert.strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
} );
-}( jQuery ) );
+}() );
-( function ( $, mw ) {
+( function () {
/**
* This module tests the input/output capabilities of the parsers of tablesorter.
* It does not test actual sorting.
// TODO add numbers sorting tests for T10115 with a different language
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
var header = [ 'Planet', 'Radius (km)' ],
// Data set "planets"
);
} );
-}( jQuery, mediaWiki ) );
+}() );
-( function ( $ ) {
+( function () {
var caretSample,
sig = {
pre: '--~~~~'
end: 11,
mode: 'set'
} );
-}( jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.ForeignApi', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
return api.post( {} );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.api.category', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
} );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
QUnit.module( 'mediawiki.api.edit', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.api.messages', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
);
} );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.api.options', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
} )
);
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
assert.strictEqual( html, '<p><b>Earth</b> is a planet.</p>', 'Parse page by Title object' );
} );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
QUnit.module( 'mediawiki.api', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
var self = this,
requests = this.requests = [];
this.api = new mw.Api();
- this.sandbox.stub( jQuery, 'ajax', function () {
+ this.sandbox.stub( $, 'ajax', function () {
var request = $.extend( {
abort: self.sandbox.spy()
}, $.Deferred() );
assert.ok( request.abort.calledOnce, 'abort request number ' + i );
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
QUnit.module( 'mediawiki.api.upload', QUnit.newMwEnvironment( {} ) );
QUnit.test( 'Basic functionality', function ( assert ) {
assert.strictEqual( $input.val(), 'Testing API upload.jpg', 'input value' );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.api.watch', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
} );
} );
-}( mediaWiki ) );
+}() );
/* eslint-disable camelcase */
/* eslint no-underscore-dangle: "off" */
-( function ( mw, $ ) {
+( function () {
var mockFilterStructure = [ {
name: 'group1',
title: 'Group 1',
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
/* eslint-disable camelcase */
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.rcfilters - FilterItem' );
QUnit.test( 'Initializing filter item', function ( assert ) {
assert.strictEqual( item.getValue(), '1', 'Value is kept as-is' );
} );
-}( mediaWiki ) );
+}() );
/* eslint-disable camelcase */
-( function ( mw, $ ) {
+( function () {
var filterDefinition = [ {
name: 'group1',
type: 'send_unselected_if_any',
} );
assert.strictEqual( model.areVisibleFiltersEmpty(), false );
} );
-}( mediaWiki, jQuery ) );
+}() );
/* eslint-disable camelcase */
-( function ( mw ) {
+( function () {
var filterDefinition = [ {
name: 'group1',
type: 'send_unselected_if_any',
'Invert parameter saved if there are namespaces.'
);
} );
-}( mediaWiki ) );
+}() );
/* eslint-disable camelcase */
-( function ( mw ) {
+( function () {
var itemData = {
params: {
param1: '1',
'Default state represented when item initialized with default:true.'
);
} );
-}( mediaWiki ) );
+}() );
-( function ( $ ) {
+( function () {
QUnit.module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
// TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
// DOM cleanup
$env.remove();
} );
-}( jQuery ) );
+}() );
QUnit.module( 'mediawiki.widgets.APIResultsQueue' );
-( function ( $, mw ) {
+( function () {
var itemCounter, FullResourceProvider, EmptyResourceProvider, SingleResultResourceProvider;
itemCounter = 0;
// Finish the async test
.then( done );
} );
-}( jQuery, mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.RegExp' );
QUnit.test( 'escape', function ( assert ) {
assert.strictEqual( mw.RegExp.escape( normal ), normal, 'Alphanumerals are left alone' );
} );
-}( mediaWiki ) );
+}() );
-( function ( $, mw ) {
+( function () {
var simpleSample, U_20AC, poop, mbSample,
trimByteLength = require( 'mediawiki.String' ).trimByteLength;
expected: '\uD800'
} );
-}( jQuery, mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/* eslint-disable camelcase */
var repeat = function ( input, multiplier ) {
return new Array( multiplier + 1 ).join( input );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.Uri', QUnit.newMwEnvironment( {
setup: function () {
this.mwUriOrg = mw.Uri;
href = uri.toString();
assert.strictEqual( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.base' );
QUnit.test( 'mw.hook - basic', function ( assert ) {
} ] );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var pluralTestcases = {
/*
* Sample:
pluralTest( langCode, tests );
}
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var NOW = 9012, // miliseconds
DEFAULT_DURATION = 5678, // seconds
assert.strictEqual( key, 'barfoo' );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.errorLogger', QUnit.newMwEnvironment() );
QUnit.test( 'installGlobalHandler', function ( assert ) {
assert.strictEqual( w.onerror( errorMessage, errorUrl, errorLine ), true,
'Global handler preserves true return from previous handler' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
var getBucket = mw.experiments.getBucket;
);
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.html' );
QUnit.test( 'escape', function ( assert ) {
);
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.inspect' );
);
} );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
/* eslint-disable camelcase */
var formatText, formatParse, formatnumTests, specialCharactersPageName, expectedListUsers,
expectedListUsersSitename, expectedLinkPagenamee, expectedEntrypoints,
'setParserDefaults is deep if requested'
);
} );
-}( mediaWiki, jQuery ) );
+}() );
* Some misc JavaScript compatibility tests,
* just to make sure the environments we run in are consistent.
*/
-( function ( $ ) {
+( function () {
QUnit.module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
QUnit.test( 'Variable with Unicode letter in name', function ( assert ) {
assert.strictEqual( $textarea.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')' );
}
} );
-}( jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
'use strict';
var grammarTests, bcp47Tests;
assert.strictEqual( mw.language.bcp47( input ), expected );
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
QUnit.module( 'mediawiki.loader', QUnit.newMwEnvironment( {
setup: function ( assert ) {
// Expose for load.mock.php
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
var TEST_MODEL = 'test-content-model';
QUnit.module( 'mediawiki.messagePoster', QUnit.newMwEnvironment( {
'Throws exception is same model is registered a second time'
);
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.requestIdleCallback', QUnit.newMwEnvironment( {
setup: function () {
var clock = this.clock = this.sandbox.useFakeTimers();
} );
}
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.storage' );
QUnit.test( 'set/get with storage support', function ( assert ) {
mw.storage.store = old;
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.template.mustache', {
beforeEach: function () {
assert.strictEqual( htmlPartial, 'Hello goodbye', 'Render with partial' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.template', {
beforeEach: function () {
);
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
var specialCharactersPageName,
// Can't mock SITENAME since jqueryMsg caches it at load
siteName = mw.config.get( 'wgSiteName' );
assert.strictEqual( mw.msg( 'int-msg' ), 'Some Other Message', 'int is resolved' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
QUnit.module( 'mediawiki.toc', QUnit.newMwEnvironment( {
setup: function () {
// Prevent live cookies from interferring with the test
return $tocList.promise();
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.track' );
QUnit.test( 'track', function ( assert ) {
[ 'unsub', { key: 2 } ]
], 'Stop when unsubscribing' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.user', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
assert.strictEqual( result.trim(), result, 'no leading or trailing whitespace' );
assert.strictEqual( result2, result, 'retained' );
} );
-}( mediaWiki ) );
+}() );
-( function ( mw, $ ) {
+( function () {
var util = require( 'mediawiki.util' ),
// Based on IPTest.php > testisIPv4
IPV4_CASES = [
} );
QUnit.test( '$content', function ( assert ) {
- assert.ok( util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
+ assert.ok( util.$content instanceof $, 'mw.util.$content instance of jQuery' );
assert.strictEqual( util.$content.length, 1, 'mw.util.$content must have length of 1' );
} );
assert.strictEqual( util.isIPv6Address( ipCase[ 1 ] ), ipCase[ 0 ], ipCase[ 2 ] );
} );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw, $ ) {
+( function () {
// Simulate square element with 20px long edges placed at (20, 20) on the page
var
'It should default to a threshold of 50px and the window\'s viewport' );
} );
-}( mediaWiki, jQuery ) );
+}() );
-( function ( mw ) {
+( function () {
QUnit.module( 'mediawiki.visibleTimeout', QUnit.newMwEnvironment( {
setup: function () {
this.sandbox.clock.tick( 50 );
assert.strictEqual( called, 1 );
} );
-}( mediaWiki ) );
+}() );