* @internal documentation reviewed 15 Mar 2010
*/
class WikiPage extends Page {
- // doDeleteArticleReal() return values. Values less than zero indicate fatal errors,
- // values greater than zero indicate that there were problems not resulting in page
- // not being deleted
-
- /**
- * Delete operation aborted by hook
- */
- const DELETE_HOOK_ABORTED = -1;
-
- /**
- * Deletion successful
- */
- const DELETE_SUCCESS = 0;
-
- /**
- * Page not found
- */
- const DELETE_NO_PAGE = 1;
-
- /**
- * No revisions found to delete
- */
- const DELETE_NO_REVISIONS = 2;
-
// Constants for $mDataLoadedFrom and related
/**
return; // page doesn't exist or is missing page_latest info
}
- $revision = Revision::newFromPageId( $this->getId(), $latest );
+ // Bug 37225: if session S1 loads the page row FOR UPDATE, the result always includes the
+ // latest changes committed. This is true even within REPEATABLE-READ transactions, where
+ // S1 normally only sees changes committed before the first S1 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.
+ $flags = ( $this->mDataLoadedFrom == self::DATA_FOR_UPDATE ) ? Revision::LOCKING_READ : 0;
+ $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
if ( $revision ) { // sanity
$this->setLastEdit( $revision );
}
$conditions,
__METHOD__ );
- $result = $dbw->affectedRows() != 0;
+ $result = $dbw->affectedRows() > 0;
if ( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
$this->setLastEdit( $revision );
* Compatibility note: this function previously returned a boolean value indicating success/failure
*/
public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
- global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
+ global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol, $wgUseNPPatrol;
# Low-level sanity check
if ( $this->mTitle->getText() === '' ) {
wfProfileOut( __METHOD__ );
return $status;
- }
-
- # Make sure the revision is either completely inserted or not inserted at all
- if ( !$wgDBtransactions ) {
- $userAbort = ignore_user_abort( true );
+ } elseif ( $oldtext === false ) {
+ # Sanity check for bug 37225
+ wfProfileOut( __METHOD__ );
+ throw new MWException( "Could not find text for current revision {$oldid}." );
}
$revision = new Revision( array(
'user_text' => $user->getName(),
'timestamp' => $now
) );
+ # Bug 37225: use accessor to get the text as Revision may trim it.
+ # After trimming, the text may be a duplicate of the current text.
+ $text = $revision->getText(); // sanity; EditPage should trim already
$changed = ( strcmp( $text, $oldtext ) != 0 );
$ok = $this->updateRevisionOn( $dbw, $revision, $oldid, $oldIsRedirect );
if ( !$ok ) {
- /* Belated edit conflict! Run away!! */
+ # Belated edit conflict! Run away!!
$status->fatal( 'edit-conflict' );
- # Delete the invalid revision if the DB is not transactional
- if ( !$wgDBtransactions ) {
- $dbw->delete( 'revision', array( 'rev_id' => $revisionId ), __METHOD__ );
- }
-
- $revisionId = 0;
$dbw->rollback( __METHOD__ );
- } else {
- global $wgUseRCPatrol;
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
- # Update recentchanges
- if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
- # Mark as patrolled if the user can do so
- $patrolled = $wgUseRCPatrol && !count(
- $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
- # Add RC row to the DB
- $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
- $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
- $revisionId, $patrolled
- );
-
- # Log auto-patrolled edits
- if ( $patrolled ) {
- PatrolLog::record( $rc, true, $user );
- }
+
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
+ # Update recentchanges
+ if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
+ # Mark as patrolled if the user can do so
+ $patrolled = $wgUseRCPatrol && !count(
+ $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
+ # Add RC row to the DB
+ $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
+ $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
+ $revisionId, $patrolled
+ );
+
+ # Log auto-patrolled edits
+ if ( $patrolled ) {
+ PatrolLog::record( $rc, true, $user );
}
- $user->incEditCount();
- $dbw->commit( __METHOD__ );
}
+ $user->incEditCount();
+ $dbw->commit( __METHOD__ );
} else {
// Bug 32948: revision ID must be set to page {{REVISIONID}} and
// related variables correctly
$revision->setId( $this->getLatest() );
}
- if ( !$wgDBtransactions ) {
- ignore_user_abort( $userAbort );
- }
-
- // Now that ignore_user_abort is restored, we can respond to fatal errors
- if ( !$status->isOK() ) {
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
# Update links tables, site stats, etc.
$this->doEditUpdates( $revision, $user, array( 'changed' => $changed,
'oldcountable' => $oldcountable ) );
) );
$revisionId = $revision->insertOn( $dbw );
+ # Bug 37225: use accessor to get the text as Revision may trim it
+ $text = $revision->getText(); // sanity; EditPage should trim already
+
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
# Update recentchanges
if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
- global $wgUseRCPatrol, $wgUseNPPatrol;
-
# Mark as patrolled if the user can do so
$patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && !count(
$this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
}
/**
- * Same as doDeleteArticleReal(), but returns more detailed success/failure status
+ * Same as doDeleteArticleReal(), but returns a simple boolean. This is kept around for
+ * backwards compatibility, if you care about error reporting you should use
+ * doDeleteArticleReal() instead.
+ *
* Deletes the article with database consistency, writes logs, purges caches
*
* @param $reason string delete reason for deletion log
public function doDeleteArticle(
$reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
) {
- return $this->doDeleteArticleReal( $reason, $suppress, $id, $commit, $error, $user )
- == WikiPage::DELETE_SUCCESS;
+ $status = $this->doDeleteArticleReal( $reason, $suppress, $id, $commit, $error, $user );
+ return $status->isGood();
}
/**
* @param $commit boolean defaults to true, triggers transaction end
* @param &$error Array of errors to append to
* @param $user User The deleting user
- * @return int: One of WikiPage::DELETE_* constants
+ * @return Status: Status object; if successful, $status->value is the log_id of the
+ * deletion log entry. If the page couldn't be deleted because it wasn't
+ * found, $status is a non-fatal 'cannotdelete' error
*/
public function doDeleteArticleReal(
$reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null
wfDebug( __METHOD__ . "\n" );
+ $status = Status::newGood();
+
if ( $this->mTitle->getDBkey() === '' ) {
- return WikiPage::DELETE_NO_PAGE;
+ $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
+ return $status;
}
$user = is_null( $user ) ? $wgUser : $user;
- if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error ) ) ) {
- return WikiPage::DELETE_HOOK_ABORTED;
+ if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$user, &$reason, &$error, &$status ) ) ) {
+ if ( $status->isOK() ) {
+ // Hook aborted but didn't set a fatal status
+ $status->fatal( 'delete-hook-aborted' );
+ }
+ return $status;
}
if ( $id == 0 ) {
$this->loadPageData( 'forupdate' );
$id = $this->getID();
if ( $id == 0 ) {
- return WikiPage::DELETE_NO_PAGE;
+ $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
+ return $status;
}
}
if ( !$ok ) {
$dbw->rollback( __METHOD__ );
- return WikiPage::DELETE_NO_REVISIONS;
+ $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
+ return $status;
}
$this->doDeleteUpdates( $id );
}
wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id ) );
- return WikiPage::DELETE_SUCCESS;
+ $status->value = $logid;
+ return $status;
}
/**
if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
$truncatedtext = $wgContLang->truncate(
str_replace( "\n", ' ', $newtext ),
- max( 0, 250
+ max( 0, 255
- strlen( wfMsgForContent( 'autoredircomment' ) )
- strlen( $rt->getFullText() )
) );