throw new MWException( "Invalid or virtual namespace $ns given." );
}
+ $page = null;
+ if ( !Hooks::run( 'WikiPageFactory', [ $title, &$page ] ) ) {
+ return $page;
+ }
+
switch ( $ns ) {
case NS_FILE:
$page = new WikiFilePage( $title );
// SELECT. Thus we need S1 to also gets the revision row FOR UPDATE; otherwise, it
// may not find it since a page row UPDATE and revision row INSERT by S2 may have
// happened after the first S1 SELECT.
- // http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read
+ // https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read
$flags = Revision::READ_LOCKING;
$revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
} elseif ( $this->mDataLoadedFrom == self::READ_LATEST ) {
$dbw->startAtomic( __METHOD__ );
if ( !$oldLatest || $oldLatest == $this->lockAndGetLatest() ) {
- $dbw->replace( 'redirect',
- [ 'rd_from' ],
+ $dbw->upsert(
+ 'redirect',
[
'rd_from' => $this->getId(),
'rd_namespace' => $rt->getNamespace(),
'rd_fragment' => $rt->getFragment(),
'rd_interwiki' => $rt->getInterwiki(),
],
+ [ 'rd_from' ],
+ [
+ 'rd_namespace' => $rt->getNamespace(),
+ 'rd_title' => $rt->getDBkey(),
+ 'rd_fragment' => $rt->getFragment(),
+ 'rd_interwiki' => $rt->getInterwiki(),
+ ],
__METHOD__
);
}
}
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- // @todo move this logic to MessageCache
- if ( $this->exists() ) {
- // NOTE: use transclusion text for messages.
- // This is consistent with MessageCache::getMsgFromNamespace()
-
- $content = $this->getContent();
- $text = $content === null ? null : $content->getWikitextForTransclusion();
-
- if ( $text === null ) {
- $text = false;
- }
- } else {
- $text = false;
- }
-
- MessageCache::singleton()->replace( $this->mTitle->getDBkey(), $text );
+ $messageCache = MessageCache::singleton();
+ $messageCache->updateMessageOverride( $this->mTitle, $this->getContent() );
}
return true;
* - 'no-change': don't update the article count, ever
*/
public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
- global $wgRCWatchCategoryMembership, $wgContLang;
+ global $wgRCWatchCategoryMembership;
$options += [
'changed' => true,
}
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- // XXX: could skip pseudo-messages like js/css here, based on content model.
- $msgtext = $content ? $content->getWikitextForTransclusion() : null;
- if ( $msgtext === false || $msgtext === null ) {
- $msgtext = '';
- }
-
- MessageCache::singleton()->replace( $shortTitle, $msgtext );
-
- if ( $wgContLang->hasVariants() ) {
- $wgContLang->updateConversionTable( $this->mTitle );
- }
+ MessageCache::singleton()->updateMessageOverride( $this->mTitle, $content );
}
if ( $options['created'] ) {
} elseif ( $options['changed'] ) { // bug 50785
self::onArticleEdit( $this->mTitle, $revision );
}
+
+ ResourceLoaderWikiModule::invalidateModuleCache(
+ $this->mTitle, $options['oldrevision'], $revision, wfWikiID()
+ );
}
/**
}
if ( !$protect ) { // No protection at all means unprotection
- $revCommentMsg = 'unprotectedarticle';
+ $revCommentMsg = 'unprotectedarticle-comment';
$logAction = 'unprotect';
} elseif ( $isProtected ) {
- $revCommentMsg = 'modifiedarticleprotection';
+ $revCommentMsg = 'modifiedarticleprotection-comment';
$logAction = 'modify';
} else {
- $revCommentMsg = 'protectedarticle';
+ $revCommentMsg = 'protectedarticle-comment';
$logAction = 'protect';
}
public function insertProtectNullRevision( $revCommentMsg, array $limit,
array $expiry, $cascade, $reason, $user = null
) {
- global $wgContLang;
$dbw = wfGetDB( DB_MASTER );
// Prepare a null revision to be added to the history
- $editComment = $wgContLang->ucfirst(
- wfMessage(
- $revCommentMsg,
- $this->mTitle->getPrefixedText()
- )->inContentLanguage()->text()
- );
+ $editComment = wfMessage(
+ $revCommentMsg,
+ $this->mTitle->getPrefixedText(),
+ $user ? $user->getName() : ''
+ )->inContentLanguage()->text();
if ( $reason ) {
$editComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
}
*/
public function doDeleteArticleReal(
$reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
- $tags = []
+ $tags = [], $logsubtype = 'delete'
) {
global $wgUser, $wgContentHandlerUseDB;
// unless they actually try to catch exceptions (which is rare).
// we need to remember the old content so we can use it to generate all deletion updates.
+ $revision = $this->getRevision();
try {
$content = $this->getContent( Revision::RAW );
} catch ( Exception $ex ) {
$content = null;
}
+ $fields = Revision::selectFields();
+ $bitfield = false;
+
// Bitfields to further suppress the content
if ( $suppress ) {
- $bitfield = 0;
- // This should be 15...
- $bitfield |= Revision::DELETED_TEXT;
- $bitfield |= Revision::DELETED_COMMENT;
- $bitfield |= Revision::DELETED_USER;
- $bitfield |= Revision::DELETED_RESTRICTED;
- $deletionFields = [ $dbw->addQuotes( $bitfield ) . ' AS deleted' ];
- } else {
- $deletionFields = [ 'rev_deleted AS deleted' ];
+ $bitfield = Revision::SUPPRESSED_ALL;
+ $fields = array_diff( $fields, [ 'rev_deleted' ] );
}
// For now, shunt the revision data into the archive table.
// the rev_deleted field, which is reserved for this purpose.
// Get all of the page revisions
- $fields = array_diff( Revision::selectFields(), [ 'rev_deleted' ] );
$res = $dbw->select(
'revision',
- array_merge( $fields, $deletionFields ),
+ $fields,
[ 'rev_page' => $id ],
__METHOD__,
'FOR UPDATE'
'ar_flags' => '',
'ar_len' => $row->rev_len,
'ar_page_id' => $id,
- 'ar_deleted' => $row->deleted,
+ 'ar_deleted' => $suppress ? $bitfield : $row->rev_deleted,
'ar_sha1' => $row->rev_sha1,
];
if ( $wgContentHandlerUseDB ) {
// Log the deletion, if the page was suppressed, put it in the suppression log instead
$logtype = $suppress ? 'suppress' : 'delete';
- $logEntry = new ManualLogEntry( $logtype, 'delete' );
+ $logEntry = new ManualLogEntry( $logtype, $logsubtype );
$logEntry->setPerformer( $user );
$logEntry->setTarget( $logTitle );
$logEntry->setComment( $reason );
$dbw->endAtomic( __METHOD__ );
- $this->doDeleteUpdates( $id, $content );
+ $this->doDeleteUpdates( $id, $content, $revision );
Hooks::run( 'ArticleDeleteComplete', [
&$wikiPageBeforeDelete,
* Do some database updates after deletion
*
* @param int $id The page_id value of the page being deleted
- * @param Content $content Optional page content to be used when determining
+ * @param Content|null $content Optional 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
*/
- public function doDeleteUpdates( $id, Content $content = null ) {
+ public function doDeleteUpdates( $id, Content $content = null, Revision $revision = null ) {
try {
$countable = $this->isCountable();
} catch ( Exception $ex ) {
// Clear caches
WikiPage::onArticleDelete( $this->mTitle );
+ ResourceLoaderWikiModule::invalidateModuleCache(
+ $this->mTitle, $revision, null, wfWikiID()
+ );
// Reset this object and the Title object
$this->loadFromRow( false, self::READ_LATEST );
* @param Title $title
*/
public static function onArticleDelete( Title $title ) {
- global $wgContLang;
-
// Update existence markers on article/talk tabs...
$other = $title->getOtherPage();
// Messages
if ( $title->getNamespace() == NS_MEDIAWIKI ) {
- MessageCache::singleton()->replace( $title->getDBkey(), false );
-
- if ( $wgContLang->hasVariants() ) {
- $wgContLang->updateConversionTable( $title );
- }
+ MessageCache::singleton()->updateMessageOverride( $title, null );
}
// Images
* Update all the appropriate counts in the category table, given that
* we've added the categories $added and deleted the categories $deleted.
*
+ * This should only be called from deferred updates or jobs to avoid contention.
+ *
* @param array $added The names of categories that were added
* @param array $deleted The names of categories that were deleted
* @param integer $id Page ID (this should be the original deleted page ID)
*/
public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
$id = $id ?: $this->getId();
+ $ns = $this->getTitle()->getNamespace();
+
+ $addFields = [ 'cat_pages = cat_pages + 1' ];
+ $removeFields = [ 'cat_pages = cat_pages - 1' ];
+ if ( $ns == NS_CATEGORY ) {
+ $addFields[] = 'cat_subcats = cat_subcats + 1';
+ $removeFields[] = 'cat_subcats = cat_subcats - 1';
+ } elseif ( $ns == NS_FILE ) {
+ $addFields[] = 'cat_files = cat_files + 1';
+ $removeFields[] = 'cat_files = cat_files - 1';
+ }
+
$dbw = wfGetDB( DB_MASTER );
- $method = __METHOD__;
- // Do this at the end of the commit to reduce lock wait timeouts
- $dbw->onTransactionPreCommitOrIdle(
- function () use ( $dbw, $added, $deleted, $id, $method ) {
- $ns = $this->getTitle()->getNamespace();
-
- $addFields = [ 'cat_pages = cat_pages + 1' ];
- $removeFields = [ 'cat_pages = cat_pages - 1' ];
- if ( $ns == NS_CATEGORY ) {
- $addFields[] = 'cat_subcats = cat_subcats + 1';
- $removeFields[] = 'cat_subcats = cat_subcats - 1';
- } elseif ( $ns == NS_FILE ) {
- $addFields[] = 'cat_files = cat_files + 1';
- $removeFields[] = 'cat_files = cat_files - 1';
- }
- if ( count( $added ) ) {
- $existingAdded = $dbw->selectFieldValues(
- 'category',
- 'cat_title',
- [ 'cat_title' => $added ],
- $method
- );
+ if ( count( $added ) ) {
+ $existingAdded = $dbw->selectFieldValues(
+ 'category',
+ 'cat_title',
+ [ 'cat_title' => $added ],
+ __METHOD__
+ );
- // For category rows that already exist, do a plain
- // UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
- // to avoid creating gaps in the cat_id sequence.
- if ( count( $existingAdded ) ) {
- $dbw->update(
- 'category',
- $addFields,
- [ 'cat_title' => $existingAdded ],
- $method
- );
- }
+ // For category rows that already exist, do a plain
+ // UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
+ // to avoid creating gaps in the cat_id sequence.
+ if ( count( $existingAdded ) ) {
+ $dbw->update(
+ 'category',
+ $addFields,
+ [ 'cat_title' => $existingAdded ],
+ __METHOD__
+ );
+ }
- $missingAdded = array_diff( $added, $existingAdded );
- if ( count( $missingAdded ) ) {
- $insertRows = [];
- foreach ( $missingAdded as $cat ) {
- $insertRows[] = [
- 'cat_title' => $cat,
- 'cat_pages' => 1,
- 'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
- 'cat_files' => ( $ns == NS_FILE ) ? 1 : 0,
- ];
- }
- $dbw->upsert(
- 'category',
- $insertRows,
- [ 'cat_title' ],
- $addFields,
- $method
- );
- }
+ $missingAdded = array_diff( $added, $existingAdded );
+ if ( count( $missingAdded ) ) {
+ $insertRows = [];
+ foreach ( $missingAdded as $cat ) {
+ $insertRows[] = [
+ 'cat_title' => $cat,
+ 'cat_pages' => 1,
+ 'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
+ 'cat_files' => ( $ns == NS_FILE ) ? 1 : 0,
+ ];
}
+ $dbw->upsert(
+ 'category',
+ $insertRows,
+ [ 'cat_title' ],
+ $addFields,
+ __METHOD__
+ );
+ }
+ }
- if ( count( $deleted ) ) {
- $dbw->update(
- 'category',
- $removeFields,
- [ 'cat_title' => $deleted ],
- $method
- );
- }
+ if ( count( $deleted ) ) {
+ $dbw->update(
+ 'category',
+ $removeFields,
+ [ 'cat_title' => $deleted ],
+ __METHOD__
+ );
+ }
- foreach ( $added as $catName ) {
- $cat = Category::newFromName( $catName );
- Hooks::run( 'CategoryAfterPageAdded', [ $cat, $this ] );
- }
+ foreach ( $added as $catName ) {
+ $cat = Category::newFromName( $catName );
+ Hooks::run( 'CategoryAfterPageAdded', [ $cat, $this ] );
+ }
- foreach ( $deleted as $catName ) {
- $cat = Category::newFromName( $catName );
- Hooks::run( 'CategoryAfterPageRemoved', [ $cat, $this, $id ] );
- }
+ foreach ( $deleted as $catName ) {
+ $cat = Category::newFromName( $catName );
+ Hooks::run( 'CategoryAfterPageRemoved', [ $cat, $this, $id ] );
+ }
- // Refresh counts on categories that should be empty now, to
- // trigger possible deletion. Check master for the most
- // up-to-date cat_pages.
- if ( count( $deleted ) ) {
- $rows = $dbw->select(
- 'category',
- [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
- [ 'cat_title' => $deleted, 'cat_pages <= 0' ],
- $method
- );
- foreach ( $rows as $row ) {
- $cat = Category::newFromRow( $row );
- $cat->refreshCounts();
- }
- }
- },
- __METHOD__
- );
+ // Refresh counts on categories that should be empty now, to
+ // trigger possible deletion. Check master for the most
+ // up-to-date cat_pages.
+ if ( count( $deleted ) ) {
+ $rows = $dbw->select(
+ 'category',
+ [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
+ [ 'cat_title' => $deleted, 'cat_pages <= 0' ],
+ __METHOD__
+ );
+ foreach ( $rows as $row ) {
+ $cat = Category::newFromRow( $row );
+ $cat->refreshCounts();
+ }
+ }
}
/**