summaryrefslogtreecommitdiff
path: root/includes/Article.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2008-08-15 01:29:47 +0200
committerPierre Schmitz <pierre@archlinux.de>2008-08-15 01:29:47 +0200
commit370e83bb0dfd0c70de268c93bf07ad5ee0897192 (patch)
tree491674f4c242e4d6ba0d04eafa305174c35a3391 /includes/Article.php
parentf4debf0f12d0524d2b2427c55ea3f16b680fad97 (diff)
Update auf 1.13.0
Diffstat (limited to 'includes/Article.php')
-rw-r--r--includes/Article.php708
1 files changed, 487 insertions, 221 deletions
diff --git a/includes/Article.php b/includes/Article.php
index 0544db7d..4d8277bb 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -1,6 +1,7 @@
<?php
/**
* File for articles
+ * @file
*/
/**
@@ -34,6 +35,8 @@ class Article {
var $mTouched; //!<
var $mUser; //!<
var $mUserText; //!<
+ var $mRedirectTarget; //!<
+ var $mIsRedirect;
/**@}}*/
/**
@@ -57,12 +60,69 @@ class Article {
}
/**
+ * If this page is a redirect, get its target
+ *
+ * The target will be fetched from the redirect table if possible.
+ * If this page doesn't have an entry there, call insertRedirect()
+ * @return mixed Title object, or null if this page is not a redirect
+ */
+ public function getRedirectTarget() {
+ if(!$this->mTitle || !$this->mTitle->isRedirect())
+ return null;
+ if(!is_null($this->mRedirectTarget))
+ return $this->mRedirectTarget;
+
+ # Query the redirect table
+ $dbr = wfGetDB(DB_SLAVE);
+ $res = $dbr->select('redirect',
+ array('rd_namespace', 'rd_title'),
+ array('rd_from' => $this->getID()),
+ __METHOD__
+ );
+ $row = $dbr->fetchObject($res);
+ if($row)
+ return $this->mRedirectTarget = Title::makeTitle($row->rd_namespace, $row->rd_title);
+
+ # This page doesn't have an entry in the redirect table
+ return $this->mRedirectTarget = $this->insertRedirect();
+ }
+
+ /**
+ * Insert an entry for this page into the redirect table.
+ *
+ * Don't call this function directly unless you know what you're doing.
+ * @return Title object
+ */
+ public function insertRedirect() {
+ $retval = Title::newFromRedirect($this->getContent());
+ if(!$retval)
+ return null;
+ $dbw = wfGetDB(DB_MASTER);
+ $dbw->replace('redirect', array('rd_from'), array(
+ 'rd_from' => $this->getID(),
+ 'rd_namespace' => $retval->getNamespace(),
+ 'rd_title' => $retval->getDBKey()
+ ), __METHOD__);
+ return $retval;
+ }
+
+ /**
+ * Get the Title object this page redirects to
+ *
* @return mixed false, Title of in-wiki target, or string with URL
*/
- function followRedirect() {
+ public function followRedirect() {
$text = $this->getContent();
+ return self::followRedirectText( $text );
+ }
+
+ /**
+ * Get the Title object this text redirects to
+ *
+ * @return mixed false, Title of in-wiki target, or string with URL
+ */
+ public function followRedirectText( $text ) {
$rt = Title::newFromRedirect( $text );
-
# process if title object is valid and not special:userlogout
if( $rt ) {
if( $rt->getInterwiki() != '' ) {
@@ -92,7 +152,6 @@ class Article {
return $rt;
}
}
-
// No or invalid redirect
return false;
}
@@ -114,6 +173,7 @@ class Article {
$this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded
$this->mRedirectedFrom = null; # Title object if set
+ $this->mRedirectTarget = null; # Title object if set
$this->mUserText =
$this->mTimestamp = $this->mComment = '';
$this->mGoodAdjustment = $this->mTotalAdjustment = 0;
@@ -138,7 +198,7 @@ class Article {
* @return Return the text of this revision
*/
function getContent() {
- global $wgUser, $wgOut;
+ global $wgUser, $wgOut, $wgMessageCache;
wfProfileIn( __METHOD__ );
@@ -147,12 +207,13 @@ class Article {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ $wgMessageCache->loadAllMessages();
$ret = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
} else {
$ret = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
}
- return "<div class='noarticletext'>$ret</div>";
+ return "<div class='noarticletext'>\n$ret\n</div>";
} else {
$this->loadContent();
wfProfileOut( __METHOD__ );
@@ -298,13 +359,13 @@ class Article {
*/
function loadPageData( $data = 'fromdb' ) {
if ( $data === 'fromdb' ) {
- $dbr = $this->getDB();
+ $dbr = wfGetDB( DB_MASTER );
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
- $lc =& LinkCache::singleton();
+ $lc = LinkCache::singleton();
if ( $data ) {
- $lc->addGoodLinkObj( $data->page_id, $this->mTitle );
+ $lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect );
$this->mTitle->mArticleID = $data->page_id;
@@ -336,15 +397,13 @@ class Article {
return $this->mContent;
}
- $dbr = $this->getDB();
+ $dbr = wfGetDB( DB_MASTER );
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
$t = $this->mTitle->getPrefixedText();
- if( $oldid ) {
- $t .= ',oldid='.$oldid;
- }
- $this->mContent = wfMsg( 'missingarticle', $t ) ;
+ $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
+ $this->mContent = wfMsg( 'missing-article', $t, $d ) ;
if( $oldid ) {
$revision = Revision::newFromId( $oldid );
@@ -377,15 +436,14 @@ class Article {
// FIXME: Horrible, horrible! This content-loading interface just plain sucks.
// We should instead work with the Revision object when we need it...
- $this->mContent = $revision->userCan( Revision::DELETED_TEXT ) ? $revision->getRawText() : "";
- //$this->mContent = $revision->getText();
+ $this->mContent = $revision->revText(); // Loads if user is allowed
$this->mUser = $revision->getUser();
$this->mUserText = $revision->getUserText();
$this->mComment = $revision->getComment();
$this->mTimestamp = wfTimestamp( TS_MW, $revision->getTimestamp() );
- $this->mRevIdFetched = $revision->getID();
+ $this->mRevIdFetched = $revision->getId();
$this->mContentLoaded = true;
$this->mRevision =& $revision;
@@ -407,8 +465,10 @@ class Article {
* Get the database which should be used for reads
*
* @return Database
+ * @deprecated - just call wfGetDB( DB_MASTER ) instead
*/
function getDB() {
+ wfDeprecated( __METHOD__ );
return wfGetDB( DB_MASTER );
}
@@ -490,6 +550,10 @@ class Article {
*/
function isRedirect( $text = false ) {
if ( $text === false ) {
+ if ( $this->mDataLoaded )
+ return $this->mIsRedirect;
+
+ // Apparently loadPageData was never called
$this->loadContent();
$titleObj = Title::newFromRedirect( $this->fetchContent() );
} else {
@@ -526,14 +590,14 @@ class Article {
$id = $this->getID();
if ( 0 == $id ) return;
- $this->mLastRevision = Revision::loadFromPageId( $this->getDB(), $id );
+ $this->mLastRevision = Revision::loadFromPageId( wfGetDB( DB_MASTER ), $id );
if( !is_null( $this->mLastRevision ) ) {
$this->mUser = $this->mLastRevision->getUser();
$this->mUserText = $this->mLastRevision->getUserText();
$this->mTimestamp = $this->mLastRevision->getTimestamp();
$this->mComment = $this->mLastRevision->getComment();
$this->mMinorEdit = $this->mLastRevision->isMinor();
- $this->mRevIdFetched = $this->mLastRevision->getID();
+ $this->mRevIdFetched = $this->mLastRevision->getId();
}
}
@@ -571,7 +635,6 @@ class Article {
}
/**
- * @todo Document, fixme $offset never used.
* @param $limit Integer: default 0.
* @param $offset Integer: default 0.
*/
@@ -593,6 +656,8 @@ class Article {
ORDER BY timestamp DESC";
if ($limit > 0) { $sql .= ' LIMIT '.$limit; }
+ if ($offset > 0) { $sql .= ' OFFSET '.$offset; }
+
$sql .= ' '. $this->getSelectOptions();
$res = $dbr->query($sql, __METHOD__);
@@ -609,7 +674,7 @@ class Article {
* This is the default action of the script: just view the page of
* the given title.
*/
- function view() {
+ function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgParser;
global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
@@ -618,7 +683,7 @@ class Article {
wfProfileIn( __METHOD__ );
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$ns = $this->mTitle->getNamespace(); # shortcut
# Get variables from query string
@@ -689,12 +754,7 @@ class Article {
}
# Should the parser cache be used?
- $pcache = $wgEnableParserCache
- && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
- && $this->exists()
- && empty( $oldid )
- && !$this->mTitle->isCssOrJsPage()
- && !$this->mTitle->isCssJsSubpage();
+ $pcache = $this->useParserCache( $oldid );
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
@@ -705,7 +765,6 @@ class Article {
// This is an internally redirected page view.
// We'll need a backlink to the source page for navigation.
if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
- $sk = $wgUser->getSkin();
$redir = $sk->makeKnownLinkObj( $this->mRedirectedFrom, '', 'redirect=no' );
$s = wfMsg( 'redirectedfrom', $redir );
$wgOut->setSubtitle( $s );
@@ -722,7 +781,6 @@ class Article {
// If it was reported from a trusted site, supply a backlink.
global $wgRedirectSources;
if( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
- $sk = $wgUser->getSkin();
$redir = $sk->makeExternalLink( $rdfrom, $rdfrom );
$s = wfMsg( 'redirectedfrom', $redir );
$wgOut->setSubtitle( $s );
@@ -740,16 +798,17 @@ class Article {
$outputDone = true;
}
}
+ # Fetch content and check for errors
if ( !$outputDone ) {
$text = $this->getContent();
if ( $text === false ) {
# Failed to load, replace text with error message
$t = $this->mTitle->getPrefixedText();
if( $oldid ) {
- $t .= ',oldid='.$oldid;
- $text = wfMsg( 'missingarticle', $t );
+ $d = wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid );
+ $text = wfMsg( 'missing-article', $t, $d );
} else {
- $text = wfMsg( 'noarticletext', $t );
+ $text = wfMsg( 'noarticletext' );
}
}
@@ -757,11 +816,11 @@ class Article {
if ( !$this->mTitle->userCanRead() ) {
$wgOut->loginToUse();
$wgOut->output();
+ wfProfileOut( __METHOD__ );
exit;
}
# We're looking at an old revision
-
if ( !empty( $oldid ) ) {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if( is_null( $this->mRevision ) ) {
@@ -772,6 +831,7 @@ class Article {
if( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-permission' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ wfProfileOut( __METHOD__ );
return;
} else {
$wgOut->addWikiMsg( 'rev-deleted-text-view' );
@@ -779,16 +839,13 @@ class Article {
}
}
}
-
}
- }
- if( !$outputDone ) {
- $wgOut->setRevisionId( $this->getRevIdFetched() );
+ $wgOut->setRevisionId( $this->getRevIdFetched() );
+
// Pages containing custom CSS or JavaScript get special treatment
if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
$wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
-
// Give hooks a chance to customise the output
if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
// Wrap the whole lot in a <pre> and don't parse
@@ -798,22 +855,9 @@ class Article {
$wgOut->addHtml( htmlspecialchars( $this->mContent ) );
$wgOut->addHtml( "\n</pre>\n" );
}
-
- }
-
- elseif ( $rt = Title::newFromRedirect( $text ) ) {
- # Display redirect
- $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
- $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
- # Don't overwrite the subtitle if this was an old revision
- if( !$wasRedirected && $this->isCurrent() ) {
- $wgOut->setSubtitle( wfMsgHtml( 'redirectpagesub' ) );
- }
- $link = $sk->makeLinkObj( $rt, $rt->getFullText() );
-
- $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
- '<span class="redirectText">'.$link.'</span>' );
-
+ } else if ( $rt = Title::newFromRedirect( $text ) ) {
+ # Don't append the subtitle if this was an old revision
+ $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() );
$parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
$wgOut->addParserOutputNoText( $parseout );
} else if ( $pcache ) {
@@ -840,7 +884,6 @@ class Article {
if( !$this->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
-
}
}
/* title may have been set from the cache */
@@ -850,8 +893,7 @@ class Article {
}
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
- if( $ns == NS_USER_TALK &&
- User::isIP( $this->mTitle->getText() ) ) {
+ if( $ns == NS_USER_TALK && IP::isValid( $this->mTitle->getText() ) ) {
$wgOut->addWikiMsg('anontalkpagetext');
}
@@ -875,6 +917,41 @@ class Article {
$this->viewUpdates();
wfProfileOut( __METHOD__ );
}
+
+ /*
+ * Should the parser cache be used?
+ */
+ protected function useParserCache( $oldid ) {
+ global $wgUser, $wgEnableParserCache;
+
+ return $wgEnableParserCache
+ && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
+ && $this->exists()
+ && empty( $oldid )
+ && !$this->mTitle->isCssOrJsPage()
+ && !$this->mTitle->isCssJsSubpage();
+ }
+
+ protected function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
+ global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
+
+ # Display redirect
+ $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+ $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
+
+ if( $appendSubtitle ) {
+ $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) );
+ }
+ $sk = $wgUser->getSkin();
+ if ( $forceKnown )
+ $link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+ else
+ $link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+
+ $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
+ '<span class="redirectText">'.$link.'</span>' );
+
+ }
function addTrackbacks() {
global $wgOut, $wgUser;
@@ -897,6 +974,7 @@ class Article {
. $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
$rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
+ $tbtext .= "\n";
$tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
$o->tb_url,
@@ -1067,7 +1145,6 @@ class Article {
$result = $dbw->affectedRows() != 0;
if ($result) {
- // FIXME: Should the result from updateRedirectOn() be returned instead?
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
@@ -1112,6 +1189,8 @@ class Article {
$dbw->delete( 'redirect', $where, __METHOD__);
}
+ if( $this->getTitle()->getNamespace() == NS_IMAGE )
+ RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
wfProfileOut( __METHOD__ );
return ( $dbw->affectedRows() != 0 );
}
@@ -1252,10 +1331,10 @@ class Article {
}
}
- $extraq = ''; // Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) );
+ $extraQuery = ''; // Give extensions a chance to modify URL query on update
+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq );
+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
}
return $good;
}
@@ -1291,11 +1370,12 @@ class Article {
* EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
* to be thrown from the Database. These two conditions are also possible with auto-detection due
* to MediaWiki's performance-optimised locking strategy.
+ * @param $baseRevId, the revision ID this edit was based off, if any
*
* @return bool success
*/
- function doEdit( $text, $summary, $flags = 0 ) {
- global $wgUser, $wgDBtransactions;
+ function doEdit( $text, $summary, $flags = 0, $baseRevId = false ) {
+ global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
wfProfileIn( __METHOD__ );
$good = true;
@@ -1325,9 +1405,10 @@ class Article {
$oldtext = $this->getContent();
$oldsize = strlen( $oldtext );
- # Provide autosummaries if one is not provided.
- if ($flags & EDIT_AUTOSUMMARY && $summary == '')
+ # Provide autosummaries if one is not provided and autosummaries are enabled.
+ if( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
$summary = $this->getAutosummary( $oldtext, $text, $flags );
+ }
$editInfo = $this->prepareTextForEdit( $text );
$text = $editInfo->pst;
@@ -1346,7 +1427,7 @@ class Article {
$lastRevision = 0;
$revisionId = 0;
-
+
$changed = ( strcmp( $text, $oldtext ) != 0 );
if ( $changed ) {
@@ -1368,7 +1449,8 @@ class Article {
'page' => $this->getId(),
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text
+ 'text' => $text,
+ 'parent_id' => $lastRevision
) );
$dbw->begin();
@@ -1382,6 +1464,8 @@ class Article {
$good = false;
$dbw->rollback();
} else {
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId ) );
+
# Update recentchanges
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
$rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
@@ -1414,7 +1498,7 @@ class Article {
# Invalidate cache of this article and all pages using this article
# as a template. Partly deferred.
Article::onArticleEdit( $this->mTitle );
-
+
# Update links tables, site stats, etc.
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
}
@@ -1446,6 +1530,8 @@ class Article {
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
$rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
@@ -1495,15 +1581,16 @@ class Article {
*
* @param boolean $noRedir Add redirect=no
* @param string $sectionAnchor section to redirect to, including "#"
+ * @param string $extraQuery, extra query params
*/
- function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
+ function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
global $wgOut;
if ( $noRedir ) {
$query = 'redirect=no';
- if( $extraq )
+ if( $extraQuery )
$query .= "&$query";
} else {
- $query = $extraq;
+ $query = $extraQuery;
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
@@ -1518,8 +1605,8 @@ class Article {
# Check patrol config options
if ( !($wgUseNPPatrol || $wgUseRCPatrol)) {
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
- return;
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ return;
}
# If we haven't been given an rc_id value, we can't do anything
@@ -1527,18 +1614,18 @@ class Article {
$rc = $rcid ? RecentChange::newFromId($rcid) : null;
if ( is_null ( $rc ) )
{
- $wgOut->errorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
+ $wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
- if ( !$wgUseRCPatrol && $rc->mAttribs['rc_type'] != RC_NEW) {
+ if ( !$wgUseRCPatrol && $rc->getAttribute( 'rc_type' ) != RC_NEW) {
// Only new pages can be patrolled if the general patrolling is off....???
// @fixme -- is this necessary? Shouldn't we only bother controlling the
// front end here?
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
+
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'patrol', $wgUser );
@@ -1554,7 +1641,7 @@ class Article {
}
#It would be nice to see where the user had actually come from, but for now just guess
- $returnto = $rc->mAttribs['rc_type'] == RC_NEW ? 'Newpages' : 'Recentchanges';
+ $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
$return = Title::makeTitle( NS_SPECIAL, $returnto );
# If it's left up to us, check that the user is allowed to patrol this edit
@@ -1575,10 +1662,14 @@ class Article {
}
}
- # Mark the edit as patrolled
- RecentChange::markPatrolled( $rcid );
- PatrolLog::record( $rcid );
- wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ # Check that the revision isn't patrolled already
+ # Prevents duplicate log entries
+ if( !$rc->getAttribute( 'rc_patrolled' ) ) {
+ # Mark the edit as patrolled
+ RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid );
+ wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ }
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
@@ -1724,8 +1815,8 @@ class Article {
$updated = Article::flattenRestrictions( $limit );
$changed = ( $current != $updated );
- $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
- $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
+ $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
# If nothing's changed, do nothing
@@ -1751,12 +1842,16 @@ class Article {
}
$comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
- foreach( $limit as $action => $restrictions ) {
- # Check if the group level required to edit also can protect pages
- # Otherwise, people who cannot normally protect can "protect" pages via transclusion
- $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && $wgGroupPermissions[$restrictions]['protect'] );
+ # Only restrictions with the 'protect' right can cascade...
+ # Otherwise, people who cannot normally protect can "protect" pages via transclusion
+ foreach( $limit as $action => $restriction ) {
+ # FIXME: can $restriction be an array or what? (same as fixme above)
+ if( $restriction != 'protect' && $restriction != 'sysop' ) {
+ $cascade = false;
+ break;
+ }
}
-
+
$cascade_description = '';
if ($cascade) {
$cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
@@ -1770,8 +1865,7 @@ class Article {
$comment .= "$expiry_description";
if ( $cascade )
$comment .= "$cascade_description";
-
- $rowsAffected = false;
+
# Update restrictions table
foreach( $limit as $action => $restrictions ) {
if ($restrictions != '' ) {
@@ -1779,18 +1873,11 @@ class Article {
array( 'pr_page' => $id, 'pr_type' => $action
, 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
, 'pr_expiry' => $encodedExpiry ), __METHOD__ );
- if($dbw->affectedRows() != 0)
- $rowsAffected = true;
} else {
$dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
'pr_type' => $action ), __METHOD__ );
- if($dbw->affectedRows() != 0)
- $rowsAffected = true;
}
}
- if(!$rowsAffected)
- // No change
- return true;
# Insert a null revision
$nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
@@ -1806,15 +1893,15 @@ class Article {
'page_id' => $id
), 'Article::protect'
);
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, false) );
wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
# Update the protection log
$log = new LogPage( 'protect' );
-
-
-
if( $protect ) {
- $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle,
+ trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -1845,7 +1932,7 @@ class Article {
}
return implode( ':', $bits );
}
-
+
/**
* Auto-generates a deletion reason
* @param bool &$hasHistory Whether the page has a history
@@ -1907,7 +1994,7 @@ class Article {
else
$reason = wfMsgForContent('excontent', '$1');
}
-
+
// Replace newlines with spaces to prevent uglyness
$contents = preg_replace("/[\n\r]/", ' ', $contents);
// Calculate the maximum amount of chars to get
@@ -1930,18 +2017,20 @@ class Article {
$confirm = $wgRequest->wasPosted() &&
$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
-
+
$this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
$this->DeleteReason = $wgRequest->getText( 'wpReason' );
-
+
$reason = $this->DeleteReasonList;
-
+
if ( $reason != 'other' && $this->DeleteReason != '') {
// Entry from drop down menu + additional comment
$reason .= ': ' . $this->DeleteReason;
} elseif ( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision');
# This code desperately needs to be totally rewritten
@@ -1950,7 +2039,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
@@ -1980,7 +2069,7 @@ class Article {
}
if( $confirm ) {
- $this->doDelete( $reason );
+ $this->doDelete( $reason, $suppress );
if( $wgRequest->getCheck( 'wpWatch' ) ) {
$this->doWatch();
} elseif( $this->mTitle->userIsWatching() ) {
@@ -2003,10 +2092,10 @@ class Article {
array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
}
}
-
- return $this->confirmDelete( '', $reason );
+
+ return $this->confirmDelete( $reason );
}
-
+
/**
* @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
*/
@@ -2018,7 +2107,7 @@ class Article {
}
return false;
}
-
+
/**
* @return int approximate revision count
*/
@@ -2079,10 +2168,9 @@ class Article {
/**
* Output deletion confirmation dialog
- * @param $par string FIXME: do we need this parameter? One Call from Article::delete with '' only.
* @param $reason string Prefilled reason
*/
- function confirmDelete( $par, $reason ) {
+ function confirmDelete( $reason ) {
global $wgOut, $wgUser, $wgContLang;
$align = $wgContLang->isRtl() ? 'left' : 'right';
@@ -2092,9 +2180,17 @@ class Article {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'confirmdeletetext' );
- $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( 'action=delete' . $par ), 'id' => 'deleteconfirm' ) ) .
- Xml::openElement( 'fieldset' ) .
- Xml::element( 'legend', array(), wfMsg( 'delete-legend' ) ) .
+ if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+ $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\"><td></td><td>";
+ $suppress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
+ $suppress .= "</td></tr>";
+ } else {
+ $suppress = '';
+ }
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
+ Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
+ Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) .
Xml::openElement( 'table' ) .
"<tr id=\"wpDeleteReasonListRow\">
<td align='$align'>" .
@@ -2102,7 +2198,7 @@ class Article {
"</td>
<td>" .
Xml::listDropDown( 'wpDeleteReasonList',
- wfMsgForContent( 'deletereason-dropdown' ),
+ wfMsgForContent( 'deletereason-dropdown' ),
wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
"</td>
</tr>
@@ -2120,6 +2216,7 @@ class Article {
Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '3' ) ) .
"</td>
</tr>
+ $suppress
<tr>
<td></td>
<td>" .
@@ -2131,6 +2228,12 @@ class Article {
Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
Xml::closeElement( 'form' );
+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
+ $skin = $wgUser->getSkin();
+ $link = $skin->makeLink ( 'MediaWiki:Deletereason-dropdown', wfMsgHtml( 'delete-edit-reasonlist' ) );
+ $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
+ }
+
$wgOut->addHTML( $form );
$this->showLogExtract( $wgOut );
}
@@ -2140,25 +2243,24 @@ class Article {
* Show relevant lines from the deletion log
*/
function showLogExtract( $out ) {
- $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
- $logViewer = new LogViewer(
- new LogReader(
- new FauxRequest(
- array( 'page' => $this->mTitle->getPrefixedText(),
- 'type' => 'delete' ) ) ) );
- $logViewer->showList( $out );
+ $out->addHtml( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract( $out, 'delete', $this->mTitle->getPrefixedText() );
}
/**
* Perform a deletion and output success or failure messages
*/
- function doDelete( $reason ) {
+ function doDelete( $reason, $suppress = false ) {
global $wgOut, $wgUser;
wfDebug( __METHOD__."\n" );
+
+ $id = $this->getId();
+
+ $error = '';
- if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
- if ( $this->doDeleteArticle( $reason ) ) {
+ if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason, &$error))) {
+ if ( $this->doDeleteArticle( $reason, $suppress ) ) {
$deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
@@ -2168,9 +2270,12 @@ class Article {
$wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
$wgOut->returnToMain( false );
- wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason));
+ wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
} else {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ if ($error = '')
+ $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ else
+ $wgOut->showFatalError( $error );
}
}
}
@@ -2180,7 +2285,7 @@ class Article {
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
- function doDeleteArticle( $reason ) {
+ function doDeleteArticle( $reason, $suppress = false ) {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
@@ -2198,6 +2303,19 @@ class Article {
$u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getContent() ), -1 );
array_push( $wgDeferredUpdateList, $u );
+ // 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;
+ } else {
+ $bitfield = 'rev_deleted';
+ }
+
+ $dbw->begin();
// For now, shunt the revision data into the archive table.
// Text is *not* removed from the text table; bulk storage
// is left intact to avoid breaking block-compression or
@@ -2221,8 +2339,9 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len',
+ 'ar_len' => 'rev_len',
'ar_page_id' => 'page_id',
+ 'ar_deleted' => $bitfield
), array(
'page_id' => $id,
'page_id = rev_page'
@@ -2232,12 +2351,25 @@ class Article {
# Delete restrictions for it
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
+ # Fix category table counts
+ $cats = array();
+ $res = $dbw->select( 'categorylinks', 'cl_to',
+ array( 'cl_from' => $id ), __METHOD__ );
+ foreach( $res as $row ) {
+ $cats []= $row->cl_to;
+ }
+ $this->updateCategoryCounts( array(), $cats );
+
# Now that it's safely backed up, delete it
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
+ $ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
+ if( !$ok ) {
+ $dbw->rollback();
+ return false;
+ }
# If using cascading deletes, we can skip some explicit deletes
if ( !$dbw->cascadingDeletes() ) {
-
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
if ($wgUseTrackbacks)
@@ -2257,19 +2389,26 @@ class Article {
if ( !$dbw->cleanupTriggers() ) {
# Clean up recentchanges entries...
- $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
+ $dbw->delete( 'recentchanges',
+ array( 'rc_namespace' => $ns, 'rc_title' => $t, 'rc_type != '.RC_LOG ),
+ __METHOD__ );
}
+ $dbw->commit();
# Clear caches
Article::onArticleDelete( $this->mTitle );
- # Log the deletion
- $log = new LogPage( 'delete' );
- $log->addEntry( 'delete', $this->mTitle, $reason );
-
# Clear the cached article id so the interface doesn't act like we exist
$this->mTitle->resetArticleID( 0 );
$this->mTitle->mArticleID = 0;
+
+ # Log the deletion, if the page was suppressed, log it at Oversight instead
+ $logtype = $suppress ? 'suppress' : 'delete';
+ $log = new LogPage( $logtype );
+
+ # Make sure logging got through
+ $log->addEntry( 'delete', $this->mTitle, $reason, array() );
+
return true;
}
@@ -2280,15 +2419,15 @@ class Article {
* performs permissions checks on $wgUser, then calls commitRollback()
* to do the dirty work
*
- * @param string $fromP - Name of the user whose edits to rollback.
+ * @param string $fromP - Name of the user whose edits to rollback.
* @param string $summary - Custom summary. Set to default summary if empty.
* @param string $token - Rollback token.
* @param bool $bot - If true, mark all reverted edits as bot.
- *
+ *
* @param array $resultDetails contains result-specific array of additional values
* 'alreadyrolled' : 'current' (rev)
* success : 'summary' (str), 'current' (rev), 'target' (rev)
- *
+ *
* @return array of errors, each error formatted as
* array(messagekey, param1, param2, ...).
* On success, the array is empty. This array can also be passed to
@@ -2310,10 +2449,10 @@ class Article {
# If there were errors, bail out now
if(!empty($errors))
return $errors;
-
+
return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
}
-
+
/**
* Backend implementation of doRollback(), please refer there for parameter
* and return value documentation
@@ -2322,9 +2461,9 @@ class Article {
* rollback to the DB Therefore, you should only call this function direct-
* ly if you want to use custom permissions checks. If you don't, use
* doRollback() instead.
- */
+ */
public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
- global $wgUseRCPatrol, $wgUser;
+ global $wgUseRCPatrol, $wgUser, $wgLang;
$dbw = wfGetDB( DB_MASTER );
if( wfReadOnly() ) {
@@ -2352,7 +2491,7 @@ class Article {
$user = intval( $current->getUser() );
$user_text = $dbw->addQuotes( $current->getUserText() );
$s = $dbw->selectRow( 'revision',
- array( 'rev_id', 'rev_timestamp' ),
+ array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
array( 'rev_page' => $current->getPage(),
"rev_user <> {$user} OR rev_user_text <> {$user_text}"
), __METHOD__,
@@ -2362,8 +2501,11 @@ class Article {
if( $s === false ) {
# No one else ever edited this page
return array(array('cantrollback'));
+ } else if( $s->rev_deleted & REVISION::DELETED_TEXT || $s->rev_deleted & REVISION::DELETED_USER ) {
+ # Only admins can see this text
+ return array(array('notvisiblerev'));
}
-
+
$set = array();
if ( $bot && $wgUser->isAllowed('markbotedits') ) {
# Mark all reverted edits as bot
@@ -2386,15 +2528,17 @@ class Article {
# Generate the edit summary if necessary
$target = Revision::newFromId( $s->rev_id );
- if( empty( $summary ) )
- {
- global $wgLang;
- $summary = wfMsgForContent( 'revertpage',
- $target->getUserText(), $from,
- $s->rev_id, $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
- $current->getId(), $wgLang->timeanddate($current->getTimestamp())
- );
+ if( empty( $summary ) ){
+ $summary = wfMsgForContent( 'revertpage' );
}
+
+ # Allow the custom summary to use the same args as the default message
+ $args = array(
+ $target->getUserText(), $from, $s->rev_id,
+ $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
+ $current->getId(), $wgLang->timeanddate($current->getTimestamp())
+ );
+ $summary = wfMsgReplaceArgs( $summary, $args );
# Save
$flags = EDIT_UPDATE;
@@ -2404,7 +2548,7 @@ class Article {
if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
$flags |= EDIT_FORCE_BOT;
- $this->doEdit( $target->getText(), $summary, $flags );
+ $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target ) );
@@ -2439,6 +2583,19 @@ class Article {
$wgOut->rateLimited();
return;
}
+ if( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ){
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $errArray = $result[0];
+ $errMsg = array_shift( $errArray );
+ $wgOut->addWikiMsgArray( $errMsg, $errArray );
+ if( isset( $details['current'] ) ){
+ $current = $details['current'];
+ if( $current->getComment() != '' ) {
+ $wgOut->addWikiMsgArray( 'editcomment', array( $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
+ }
+ }
+ return;
+ }
# Display permissions errors before read-only message -- there's no
# point in misleading the user into thinking the inability to rollback
# is only temporary.
@@ -2469,6 +2626,11 @@ class Article {
. $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
$wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
$wgOut->returnToMain( false, $this->mTitle );
+
+ if( !$wgRequest->getBool( 'hidediff', false ) ) {
+ $de = new DifferenceEngine( $this->mTitle, $current->getId(), 'next', false, true );
+ $de->showDiff( '', '' );
+ }
}
@@ -2477,11 +2639,12 @@ class Article {
* @private
*/
function viewUpdates() {
- global $wgDeferredUpdateList;
+ global $wgDeferredUpdateList, $wgUser;
if ( 0 != $this->getID() ) {
+ # Don't update page view counters on views from bot users (bug 14044)
global $wgDisableCounters;
- if( !$wgDisableCounters ) {
+ if( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) ) {
Article::incViewCount( $this->getID() );
$u = new SiteStatsUpdate( 1, 0, 0 );
array_push( $wgDeferredUpdateList, $u );
@@ -2489,7 +2652,6 @@ class Article {
}
# Update newtalk / watchlist notification status
- global $wgUser;
$wgUser->clearNotification( $this->mTitle );
}
@@ -2546,7 +2708,7 @@ class Article {
# Save it to the parser cache
if ( $wgEnableParserCache ) {
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$parserCache->save( $editInfo->output, $this, $wgUser );
}
@@ -2646,7 +2808,7 @@ class Article {
$sk = $wgUser->getSkin();
$lnk = $current
? wfMsg( 'currentrevisionlink' )
- : $lnk = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
$curdiff = $current
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=cur&oldid='.$oldid );
@@ -2664,16 +2826,38 @@ class Article {
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
- $userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
- . $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
+ $cdel='';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( $revision->isCurrent() ) {
+ // We don't handle top deleted edits too well
+ $cdel = wfMsgHtml('rev-delundel');
+ } else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $cdel = wfMsgHtml('rev-delundel');
+ } else {
+ $cdel = $sk->makeKnownLinkObj( $revdel,
+ wfMsgHtml('rev-delundel'),
+ 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $oldid ) );
+ // Bolden oversighted content
+ if( $revision->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $cdel = "<strong>$cdel</strong>";
+ }
+ $cdel = "(<small>$cdel</small>) ";
+ }
+ # Show user links if allowed to see them. Normally they
+ # are hidden regardless, but since we can already see the text here...
+ $userlinks = $sk->revUserTools( $revision, false );
$m = wfMsg( 'revision-info-current' );
$infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
? 'revision-info-current'
: 'revision-info';
-
+
$r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
- "\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
+
+ "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -2731,9 +2915,9 @@ class Article {
$printable = $wgRequest->getVal( 'printable' );
$page = $wgRequest->getVal( 'page' );
- //check for non-standard user language; this covers uselang,
+ //check for non-standard user language; this covers uselang,
//and extensions for auto-detecting user language.
- $ulang = $wgLang->getCode();
+ $ulang = $wgLang->getCode();
$clang = $wgContLang->getCode();
$cacheable = $wgUseFileCache
@@ -2814,6 +2998,8 @@ class Article {
$revision->insertOn( $dbw );
$this->updateRevisionOn( $dbw, $revision );
$dbw->commit();
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
wfProfileOut( __METHOD__ );
}
@@ -2911,6 +3097,15 @@ class Article {
static function onArticleDelete( $title ) {
global $wgUseFileCache, $wgMessageCache;
+ // Update existence markers on article/talk tabs...
+ if( $title->isTalkPage() ) {
+ $other = $title->getSubjectPage();
+ } else {
+ $other = $title->getTalkPage();
+ }
+ $other->invalidateCache();
+ $other->purgeSquid();
+
$title->touchLinks();
$title->purgeSquid();
@@ -2920,13 +3115,20 @@ class Article {
@unlink( $cm->fileCacheName() );
}
- if( $title->getNamespace() == NS_MEDIAWIKI) {
+ # Messages
+ if( $title->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $title->getDBkey(), false );
}
+ # Images
if( $title->getNamespace() == NS_IMAGE ) {
$update = new HTMLCacheUpdate( $title, 'imagelinks' );
$update->doUpdate();
}
+ # User talk pages
+ if( $title->getNamespace() == NS_USER_TALK ) {
+ $user = User::newFromName( $title->getText(), false );
+ $user->setNewtalk( false );
+ }
}
/**
@@ -2940,7 +3142,7 @@ class Article {
// Invalidate the caches of all pages which redirect here
$wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
-
+
# Purge squid for this page only
$title->purgeSquid();
@@ -2954,6 +3156,15 @@ class Article {
/**#@-*/
/**
+ * Overriden by ImagePage class, only present here to avoid a fatal error
+ * Called for ?action=revert
+ */
+ public function revert(){
+ global $wgOut;
+ $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
+ }
+
+ /**
* Info about this page
* Called for ?action=info when $wgAllowPageInfo is on.
*
@@ -3080,38 +3291,33 @@ class Article {
}
/**
- * Return an auto-generated summary if the text provided is a redirect.
+ * Returns a list of hidden categories this page is a member of.
+ * Uses the page_props and categorylinks tables.
*
- * @param string $text The wikitext to check
- * @return string '' or an appropriate summary
+ * @return array Array of Title objects
*/
- public static function getRedirectAutosummary( $text ) {
- $rt = Title::newFromRedirect( $text );
- if( is_object( $rt ) )
- return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
- else
- return '';
- }
+ function getHiddenCategories() {
+ $result = array();
+ $id = $this->mTitle->getArticleID();
+ if( $id == 0 ) {
+ return array();
+ }
- /**
- * Return an auto-generated summary if the new text is much shorter than
- * the old text.
- *
- * @param string $oldtext The previous text of the page
- * @param string $text The submitted text of the page
- * @return string An appropriate autosummary, or an empty string.
- */
- public static function getBlankingAutosummary( $oldtext, $text ) {
- if ($oldtext!='' && $text=='') {
- return wfMsgForContent('autosumm-blank');
- } elseif (strlen($oldtext) > 10 * strlen($text) && strlen($text) < 500) {
- #Removing more than 90% of the article
- global $wgContLang;
- $truncatedtext = $wgContLang->truncate($text, max(0, 200 - strlen(wfMsgForContent('autosumm-replace'))), '...');
- return wfMsgForContent('autosumm-replace', $truncatedtext);
- } else {
- return '';
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
+ array( 'cl_to' ),
+ array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
+ 'page_namespace' => NS_CATEGORY, 'page_title=cl_to'),
+ 'Article:getHiddenCategories' );
+ if ( false !== $res ) {
+ if ( $dbr->numRows( $res ) ) {
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
+ }
+ }
}
+ $dbr->freeResult( $res );
+ return $result;
}
/**
@@ -3122,38 +3328,42 @@ class Article {
* @return string An appropriate autosummary, or an empty string.
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
+ # Decide what kind of autosummary is needed.
- # This code is UGLY UGLY UGLY.
- # Somebody PLEASE come up with a more elegant way to do it.
-
- #Redirect autosummaries
- $summary = self::getRedirectAutosummary( $newtext );
-
- if ($summary)
- return $summary;
-
- #Blanking autosummaries
- if (!($flags & EDIT_NEW))
- $summary = self::getBlankingAutosummary( $oldtext, $newtext );
-
- if ($summary)
- return $summary;
+ # Redirect autosummaries
+ $rt = Title::newFromRedirect( $newtext );
+ if( is_object( $rt ) ) {
+ return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
+ }
- #New page autosummaries
- if ($flags & EDIT_NEW && strlen($newtext)) {
- #If they're making a new article, give its text, truncated, in the summary.
+ # New page autosummaries
+ if( $flags & EDIT_NEW && strlen( $newtext ) ) {
+ # If they're making a new article, give its text, truncated, in the summary.
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
str_replace("\n", ' ', $newtext),
max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new') ) ),
'...' );
- $summary = wfMsgForContent( 'autosumm-new', $truncatedtext );
+ return wfMsgForContent( 'autosumm-new', $truncatedtext );
}
- if ($summary)
- return $summary;
+ # Blanking autosummaries
+ if( $oldtext != '' && $newtext == '' ) {
+ return wfMsgForContent('autosumm-blank');
+ } elseif( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500) {
+ # Removing more than 90% of the article
+ global $wgContLang;
+ $truncatedtext = $wgContLang->truncate(
+ $newtext,
+ max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ),
+ '...'
+ );
+ return wfMsgForContent( 'autosumm-replace', $truncatedtext );
+ }
- return $summary;
+ # If we reach this point, there's no applicable autosummary for our case, so our
+ # autosummary is empty.
+ return '';
}
/**
@@ -3175,7 +3385,7 @@ class Article {
$popts->setTidy(false);
$popts->enableLimitReport( false );
if ( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$parserCache->save( $parserOutput, $this, $wgUser );
}
@@ -3235,4 +3445,60 @@ class Article {
$wgOut->addParserOutput( $parserOutput );
}
+ /**
+ * Update all the appropriate counts in the category table, given that
+ * we've added the categories $added and deleted the categories $deleted.
+ *
+ * @param $added array The names of categories that were added
+ * @param $deleted array The names of categories that were deleted
+ * @return null
+ */
+ public function updateCategoryCounts( $added, $deleted ) {
+ $ns = $this->mTitle->getNamespace();
+ $dbw = wfGetDB( DB_MASTER );
+
+ # First make sure the rows exist. If one of the "deleted" ones didn't
+ # exist, we might legitimately not create it, but it's simpler to just
+ # create it and then give it a negative value, since the value is bogus
+ # anyway.
+ #
+ # Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
+ $insertCats = array_merge( $added, $deleted );
+ if( !$insertCats ) {
+ # Okay, nothing to do
+ return;
+ }
+ $insertRows = array();
+ foreach( $insertCats as $cat ) {
+ $insertRows[] = array( 'cat_title' => $cat );
+ }
+ $dbw->insert( 'category', $insertRows, __METHOD__, 'IGNORE' );
+
+ $addFields = array( 'cat_pages = cat_pages + 1' );
+ $removeFields = array( 'cat_pages = cat_pages - 1' );
+ if( $ns == NS_CATEGORY ) {
+ $addFields[] = 'cat_subcats = cat_subcats + 1';
+ $removeFields[] = 'cat_subcats = cat_subcats - 1';
+ } elseif( $ns == NS_IMAGE ) {
+ $addFields[] = 'cat_files = cat_files + 1';
+ $removeFields[] = 'cat_files = cat_files - 1';
+ }
+
+ if ( $added ) {
+ $dbw->update(
+ 'category',
+ $addFields,
+ array( 'cat_title' => $added ),
+ __METHOD__
+ );
+ }
+ if ( $deleted ) {
+ $dbw->update(
+ 'category',
+ $removeFields,
+ array( 'cat_title' => $deleted ),
+ __METHOD__
+ );
+ }
+ }
}