Avoid deprecation warnings in test cases.
[lhc/web/wiklou.git] / includes / WikiPage.php
index 23fbce2..333b34b 100644 (file)
@@ -33,53 +33,9 @@ abstract class Page {}
  *
  * @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;
-
+class WikiPage extends Page implements IDBAccessObject {
        // Constants for $mDataLoadedFrom and related
 
-       /**
-        * Data has not been loaded yet (or the object was cleared)
-        */
-       const DATA_NOT_LOADED = 0;
-
-       /**
-        * Data has been loaded from a slave database
-        */
-       const DATA_FROM_SLAVE = 1;
-
-       /**
-        * Data has been loaded from the master database
-        */
-       const DATA_FROM_MASTER = 2;
-
-       /**
-        * Data has been loaded from the master database using FOR UPDATE
-        */
-       const DATA_FOR_UPDATE = 3;
-
        /**
         * @var Title
         */
@@ -95,9 +51,9 @@ class WikiPage extends Page {
        /**@}}*/
 
        /**
-        * @var int; one of the DATA_* constants
+        * @var int; one of the READ_* constants
         */
-       protected $mDataLoadedFrom = self::DATA_NOT_LOADED;
+       protected $mDataLoadedFrom = self::READ_NONE;
 
        /**
         * @var Title
@@ -166,14 +122,14 @@ class WikiPage extends Page {
         *
         * @param $id Int article ID to load
         * @param $from string|int one of the following values:
-        *        - "fromdb" or self::DATA_FROM_SLAVE to select from a slave database
-        *        - "fromdbmaster" or self::DATA_FROM_MASTER to select from the master database
+        *        - "fromdb" or WikiPage::READ_NORMAL to select from a slave database
+        *        - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database
         *
         * @return WikiPage|null
         */
        public static function newFromID( $id, $from = 'fromdb' ) {
                $from = self::convertSelectType( $from );
-               $db = wfGetDB( $from === self::DATA_FROM_MASTER ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
                $row = $db->selectRow( 'page', self::selectFields(), array( 'page_id' => $id ), __METHOD__ );
                if ( !$row ) {
                        return null;
@@ -188,9 +144,9 @@ class WikiPage extends Page {
         * @param $row object: database row containing at least fields returned
         *        by selectFields().
         * @param $from string|int: source of $data:
-        *        - "fromdb" or self::DATA_FROM_SLAVE: from a slave DB
-        *        - "fromdbmaster" or self::DATA_FROM_MASTER: from the master DB
-        *        - "forupdate" or self::DATA_FOR_UPDATE: from the master DB using SELECT FOR UPDATE
+        *        - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
+        *        - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
+        *        - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
         * @return WikiPage
         */
        public static function newFromRow( $row, $from = 'fromdb' ) {
@@ -200,7 +156,7 @@ class WikiPage extends Page {
        }
 
        /**
-        * Convert 'fromdb', 'fromdbmaster' and 'forupdate' to DATA_* constants.
+        * Convert 'fromdb', 'fromdbmaster' and 'forupdate' to READ_* constants.
         *
         * @param $type object|string|int
         * @return mixed
@@ -208,11 +164,11 @@ class WikiPage extends Page {
        private static function convertSelectType( $type ) {
                switch ( $type ) {
                case 'fromdb':
-                       return self::DATA_FROM_SLAVE;
+                       return self::READ_NORMAL;
                case 'fromdbmaster':
-                       return self::DATA_FROM_MASTER;
+                       return self::READ_LATEST;
                case 'forupdate':
-                       return self::DATA_FOR_UPDATE;
+                       return self::READ_LOCKING;
                default:
                        // It may already be an integer or whatever else
                        return $type;
@@ -261,7 +217,7 @@ class WikiPage extends Page {
         */
        public function clear() {
                $this->mDataLoaded = false;
-               $this->mDataLoadedFrom = self::DATA_NOT_LOADED;
+               $this->mDataLoadedFrom = self::READ_NONE;
 
                $this->clearCacheFields();
        }
@@ -363,9 +319,9 @@ class WikiPage extends Page {
         *
         * @param $from object|string|int One of the following:
         *        - A DB query result object
-        *        - "fromdb" or self::DATA_FROM_SLAVE to get from a slave DB
-        *        - "fromdbmaster" or self::DATA_FROM_MASTER to get from the master DB
-        *        - "forupdate"  or self::DATA_FOR_UPDATE to get from the master DB using SELECT FOR UPDATE
+        *        - "fromdb" or WikiPage::READ_NORMAL to get from a slave DB
+        *        - "fromdbmaster" or WikiPage::READ_LATEST to get from the master DB
+        *        - "forupdate"  or WikiPage::READ_LOCKING to get from the master DB using SELECT FOR UPDATE
         *
         * @return void
         */
@@ -376,25 +332,25 @@ class WikiPage extends Page {
                        return;
                }
 
-               if ( $from === self::DATA_FOR_UPDATE ) {
+               if ( $from === self::READ_LOCKING ) {
                        $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle, array( 'FOR UPDATE' ) );
-               } elseif ( $from === self::DATA_FROM_MASTER ) {
+               } elseif ( $from === self::READ_LATEST ) {
                        $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
-               } elseif ( $from === self::DATA_FROM_SLAVE ) {
+               } elseif ( $from === self::READ_NORMAL ) {
                        $data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
                        # Use a "last rev inserted" timestamp key to dimish the issue of slave lag.
                        # Note that DB also stores the master position in the session and checks it.
                        $touched = $this->getCachedLastEditTime();
                        if ( $touched ) { // key set
                                if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
-                                       $from = self::DATA_FROM_MASTER;
+                                       $from = self::READ_LATEST;
                                        $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
                                }
                        }
                } else {
                        // No idea from where the caller got this data, assume slave database.
                        $data = $from;
-                       $from = self::DATA_FROM_SLAVE;
+                       $from = self::READ_NORMAL;
                }
 
                $this->loadFromRow( $data, $from );
@@ -407,9 +363,9 @@ class WikiPage extends Page {
         * @param $data object: database row containing at least fields returned
         *        by selectFields()
         * @param $from string|int One of the following:
-        *        - "fromdb" or self::DATA_FROM_SLAVE if the data comes from a slave DB
-        *        - "fromdbmaster" or self::DATA_FROM_MASTER if the data comes from the master DB
-        *        - "forupdate"  or self::DATA_FOR_UPDATE if the data comes from from
+        *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
+        *        - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
+        *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from from
         *          the master DB using SELECT FOR UPDATE
         */
        public function loadFromRow( $data, $from ) {
@@ -512,7 +468,8 @@ class WikiPage extends Page {
                        if ( $rev !== null ) {
                                return $rev->getContentModel();
                        } else {
-                               wfWarn( "Page exists but has no revision!" );
+                               $title = $this->mTitle->getPrefixedDBkey();
+                               wfWarn( "Page $title exists but has no (visible) revisions!" );
                        }
                }
 
@@ -606,7 +563,14 @@ class WikiPage extends Page {
                        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::READ_LOCKING ) ? Revision::READ_LOCKING : 0;
+               $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
                if ( $revision ) { // sanity
                        $this->setLastEdit( $revision );
                }
@@ -994,10 +958,10 @@ class WikiPage extends Page {
                $tables = array( 'revision', 'user' );
 
                $fields = array(
-                       'rev_user as user_id',
-                       'rev_user_text AS user_name',
+                       'user_id' => 'rev_user',
+                       'user_name' => 'rev_user_text',
                        $realNameField,
-                       'MAX(rev_timestamp) AS timestamp',
+                       'timestamp' => 'MAX(rev_timestamp)',
                );
 
                $conds = array( 'rev_page' => $this->getId() );
@@ -1288,7 +1252,7 @@ class WikiPage extends Page {
                        $conditions,
                        __METHOD__ );
 
-               $result = $dbw->affectedRows() != 0;
+               $result = $dbw->affectedRows() > 0;
                if ( $result ) {
                        $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
                        $this->setLastEdit( $revision );
@@ -1296,7 +1260,8 @@ class WikiPage extends Page {
                        $this->mLatest = $revision->getId();
                        $this->mIsRedirect = (bool)$rt;
                        # Update the LinkCache.
-                       LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect, $this->mLatest, $revision->getContentModel() );
+                       LinkCache::singleton()->addGoodLinkObj( $this->getId(), $this->mTitle, $len, $this->mIsRedirect,
+                                                                                                       $this->mLatest, $revision->getContentModel() );
                }
 
                wfProfileOut( __METHOD__ );
@@ -1379,6 +1344,21 @@ class WikiPage extends Page {
                return $ret;
        }
 
+    /**
+     * Get the content that needs to be saved in order to undo all revisions
+     * between $undo and $undoafter. Revisions must belong to the same page,
+     * must exist and must not be deleted
+     * @param $undo Revision
+     * @param $undoafter Revision Must be an earlier revision than $undo
+     * @return mixed string on success, false on failure
+     * @since 1.WD
+     * Before we had the Content object, this was done in getUndoText
+     */
+    public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
+        $handler = $undo->getContentHandler();
+        return $handler->getUndoContent( $this->getRevision(), $undo, $undoafter );
+    }
+
        /**
         * Get the text that needs to be saved in order to undo all revisions
         * between $undo and $undoafter. Revisions must belong to the same page,
@@ -1432,7 +1412,8 @@ class WikiPage extends Page {
                        throw new MWException( "sections not supported for content model " . $this->getContentHandler()->getModelID() );
                }
 
-               $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); # could even make section title, but that's not required.
+               # could even make section title, but that's not required.
+               $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() );
 
                $newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle, $edittime );
 
@@ -1565,8 +1546,9 @@ class WikiPage extends Page {
         *  Compatibility note: this function previously returned a boolean value indicating success/failure
         *
         * @deprecated since 1.WD: use doEditContent() instead.
+        * @todo: use doEditContent() instead everywhere
         */
-       public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) { #@todo: use doEditContent() instead
+       public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
                wfDeprecated( __METHOD__, '1.WD' );
 
                $content = ContentHandler::makeContent( $text, $this->getTitle() );
@@ -1623,7 +1605,7 @@ class WikiPage extends Page {
         */
        public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
                                                                   User $user = null, $serialisation_format = null ) {
-               global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
+               global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol, $wgUseNPPatrol;
 
                # Low-level sanity check
                if ( $this->mTitle->getText() === '' ) {
@@ -1642,24 +1624,13 @@ class WikiPage extends Page {
 
                $flags = $this->checkFlags( $flags );
 
-               # call legacy hook
-               $hook_ok = wfRunHooks( 'ArticleContentSave', array( &$this, &$user, &$content, &$summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, &$status ) );
-
-               if ( $hook_ok && Hooks::isRegistered( 'ArticleSave' ) ) { # avoid serialization overhead if the hook isn't present
-                       $content_text = $content->serialize();
-                       $txt = $content_text; # clone
-
-                       $hook_ok = wfRunHooks( 'ArticleSave', array( &$this, &$user, &$txt, &$summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, &$status ) ); #TODO: survey extensions using this hook
+               # handle hook
+               $hook_args = array( &$this, &$user, &$content, &$summary,
+                                                       $flags & EDIT_MINOR, null, null, &$flags, &$status );
 
-                       if ( $txt !== $content_text ) {
-                               # if the text changed, unserialize the new version to create an updated Content object.
-                               $content = $content->getContentHandler()->unserializeContent( $txt );
-                       }
-               }
+               if ( !wfRunHooks( 'ArticleContentSave', $hook_args )
+                       || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args ) ) {
 
-               if ( !$hook_ok ) {
                        wfDebug( __METHOD__ . ": ArticleSave or ArticleSaveContent hook aborted save!\n" );
 
                        if ( $status->isOK() ) {
@@ -1709,6 +1680,10 @@ class WikiPage extends Page {
 
                                wfProfileOut( __METHOD__ );
                                return $status;
+                       } elseif ( !$old_content ) {
+                               # Sanity check for bug 37225
+                               wfProfileOut( __METHOD__ );
+                               throw new MWException( "Could not find text for current revision {$oldid}." );
                        }
 
                        $revision = new Revision( array(
@@ -1725,6 +1700,10 @@ class WikiPage extends Page {
                                'content_format' => $serialisation_format,
                        ) ); #XXX: pass content object?!
 
+                       # 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.
+                       $content = $revision->getContent(); // sanity; EditPage should trim already
+
                        $changed = !$content->equals( $old_content );
 
                        if ( $changed ) {
@@ -1756,45 +1735,40 @@ class WikiPage extends Page {
                                $ok = $this->updateRevisionOn( $dbw, $revision, $oldid, $oldIsRedirect );
 
                                if ( !$ok ) {
-                                       /* Belated edit conflict! Run away!! */
+                                       # Belated edit conflict! Run away!!
                                        $status->fatal( 'edit-conflict' );
 
-                                       $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() );
                        }
 
-                       // 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,
@@ -1857,6 +1831,9 @@ class WikiPage extends Page {
                        ) );
                        $revisionId = $revision->insertOn( $dbw );
 
+                       # Bug 37225: use accessor to get the text as Revision may trim it
+                       $content = $revision->getContent(); // sanity; get normalized version
+
                        # Update the page record with revision data
                        $this->updateRevisionOn( $dbw, $revision, 0 );
 
@@ -1864,8 +1841,6 @@ class WikiPage extends Page {
 
                        # 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 ) );
@@ -1884,11 +1859,11 @@ class WikiPage extends Page {
                        # Update links, etc.
                        $this->doEditUpdates( $revision, $user, array( 'created' => true ) );
 
-                       wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $serialized, $summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+                       $hook_args = array( &$this, &$user, $content, $summary,
+                                                               $flags & EDIT_MINOR, null, null, &$flags, $revision );
 
-                       wfRunHooks( 'ArticleContentInsertComplete', array( &$this, &$user, $content, $summary,
-                               $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+                       ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
+                       wfRunHooks( 'ArticleContentInsertComplete', $hook_args );
                }
 
                # Do updates right now unless deferral was requested
@@ -1899,11 +1874,11 @@ class WikiPage extends Page {
                // Return the new revision (or null) to the caller
                $status->value['revision'] = $revision;
 
-               wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $serialized, $summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
+               $hook_args = array( &$this, &$user, $content, $summary,
+                                                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
 
-               wfRunHooks( 'ArticleContentSaveComplete', array( &$this, &$user, $content, $summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
+               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
+               wfRunHooks( 'ArticleContentSaveComplete', $hook_args );
 
                # Promote user to any groups they meet the criteria for
                $user->addAutopromoteOnceGroups( 'onEdit' );
@@ -2037,8 +2012,7 @@ class WikiPage extends Page {
                }
 
                # Update the links tables and other secondary data
-               $contentHandler = $revision->getContentHandler();
-               $updates = $contentHandler->getSecondaryDataUpdates( $content, $this->getTitle(), null, true, $editInfo->output );
+               $updates = $content->getSecondaryDataUpdates( $this->getTitle(), null, true, $editInfo->output );
                DataUpdate::runUpdates( $updates );
 
                wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) );
@@ -2083,7 +2057,8 @@ class WikiPage extends Page {
                }
 
                DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) );
-               DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) ); #TODO: let the search engine decide what to do with the content object
+               DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) );
+               #@TODO: let the search engine decide what to do with the content object
 
                # If this is another user's talk page, update newtalk.
                # Don't do this if $options['changed'] = false (null-edits) nor if
@@ -2099,9 +2074,9 @@ class WikiPage extends Page {
                                        wfDebug( __METHOD__ . ": invalid username\n" );
                                } elseif ( User::isIP( $shortTitle ) ) {
                                        // An anonymous user
-                                       $other->setNewtalk( true );
+                                       $other->setNewtalk( true, $revision );
                                } elseif ( $other->isLoggedIn() ) {
-                                       $other->setNewtalk( true );
+                                       $other->setNewtalk( true, $revision );
                                } else {
                                        wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
                                }
@@ -2109,7 +2084,8 @@ class WikiPage extends Page {
                }
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
-                       $msgtext = $content->getWikitextForTransclusion(); #XXX: could skip pseudo-messages like js/css here, based on content model.
+                       #XXX: could skip pseudo-messages like js/css here, based on content model.
+                       $msgtext = $content->getWikitextForTransclusion();
                        if ( $msgtext === false || $msgtext === null ) $msgtext = '';
 
                        MessageCache::singleton()->replace( $shortTitle, $msgtext );
@@ -2408,15 +2384,15 @@ class WikiPage extends Page {
        }
 
        /**
-        * 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
-        * @param $suppress int bitfield
-        *      Revision::DELETED_TEXT
-        *      Revision::DELETED_COMMENT
-        *      Revision::DELETED_USER
-        *      Revision::DELETED_RESTRICTED
+        * @param $suppress boolean suppress all revisions and log the deletion in
+        *        the suppression log instead of the deletion log
         * @param $id int article ID
         * @param $commit boolean defaults to true, triggers transaction end
         * @param &$error Array of errors to append to
@@ -2426,25 +2402,25 @@ class WikiPage extends Page {
        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();
        }
 
        /**
         * Back-end article deletion
         * Deletes the article with database consistency, writes logs, purges caches
         *
+        * @since 1.19
+        *
         * @param $reason string delete reason for deletion log
-        * @param $suppress int bitfield
-        *      Revision::DELETED_TEXT
-        *      Revision::DELETED_COMMENT
-        *      Revision::DELETED_USER
-        *      Revision::DELETED_RESTRICTED
-        * @param $id int article ID
+        * @param $suppress boolean suppress all revisions and log the deletion in
+        *        the suppression log instead of the deletion log
         * @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
@@ -2453,20 +2429,28 @@ class WikiPage extends Page {
 
                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;
                        }
                }
 
@@ -2536,7 +2520,8 @@ class WikiPage extends Page {
 
                if ( !$ok ) {
                        $dbw->rollback( __METHOD__ );
-                       return WikiPage::DELETE_NO_REVISIONS;
+                       $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
+                       return $status;
                }
 
                $this->doDeleteUpdates( $id, $content );
@@ -2556,7 +2541,8 @@ class WikiPage extends Page {
                }
 
                wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$user, $reason, $id ) );
-               return WikiPage::DELETE_SUCCESS;
+               $status->value = $logid;
+               return $status;
        }
 
        /**
@@ -3165,7 +3151,7 @@ class WikiPage extends Page {
        }
 
        /**
-        * Returns a list of updates to be performed when this page is deleted. The updates should remove any infomration
+        * Returns a list of updates to be performed when this page is deleted. The 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
@@ -3178,9 +3164,13 @@ class WikiPage extends Page {
                        $content = $this->getContent( Revision::RAW );
                }
 
-               $updates = $this->getContentHandler()->getDeletionUpdates( $content, $this->mTitle );
+               if ( !$content ) {
+                       $updates = array();
+               } else {
+                       $updates = $content->getDeletionUpdates( $this->mTitle );
+               }
 
-               wfRunHooks( 'WikiPageDeletionUpdates', array( $this, &$updates ) );
+               wfRunHooks( 'WikiPageDeletionUpdates', array( $this, $content, &$updates ) );
                return $updates;
        }
 
@@ -3286,20 +3276,24 @@ class PoolWorkArticleView extends PoolCounterWork {
        function doWork() {
                global $wgUseFileCache;
 
-               // @todo: several of the methods called on $this->page are not declared in Page, but present in WikiPage and delegated by Article.
+               // @todo: several of the methods called on $this->page are not declared in Page, but present
+               //        in WikiPage and delegated by Article.
 
                $isCurrent = $this->revid === $this->page->getLatest();
 
                if ( $this->content !== null ) {
                        $content = $this->content;
                } elseif ( $isCurrent ) {
-                       $content = $this->page->getContent( Revision::RAW ); #XXX: why use RAW audience here, and PUBLIC (default) below?
+                       #XXX: why use RAW audience here, and PUBLIC (default) below?
+                       $content = $this->page->getContent( Revision::RAW );
                } else {
                        $rev = Revision::newFromTitle( $this->page->getTitle(), $this->revid );
                        if ( $rev === null ) {
                                return false;
                        }
-                       $content = $rev->getContent(); #XXX: why use PUBLIC audience here (default), and RAW above?
+
+                       #XXX: why use PUBLIC audience here (default), and RAW above?
+                       $content = $rev->getContent();
                }
 
                $time = - microtime( true );