Fixup for r102997. If you add returns to a profiled function,
[lhc/web/wiklou.git] / includes / Article.php
index 546e2d9..3b22b86 100644 (file)
@@ -33,7 +33,6 @@ class Article extends Page {
        protected $mPage;
 
        /**
-        * @protected
         * @var ParserOptions: ParserOptions object for $wgUser articles
         */
        public $mParserOptions;
@@ -86,6 +85,7 @@ class Article extends Page {
        /**
         * Constructor from a page id
         * @param $id Int article ID to load
+        * @return Article|null
         */
        public static function newFromID( $id ) {
                $t = Title::newFromID( $id );
@@ -145,7 +145,6 @@ class Article extends Page {
 
        /**
         * Clear the object
-        * @private
         */
        public function clear() {
                $this->mContentLoaded = false;
@@ -187,7 +186,7 @@ class Article extends Page {
 
                        return $text;
                } else {
-                       $this->loadContent();
+                       $this->fetchContent();
                        wfProfileOut( __METHOD__ );
 
                        return $this->mContent;
@@ -216,27 +215,37 @@ class Article extends Page {
 
                $this->mRedirectUrl = false;
 
-               $oldid = $wgRequest->getVal( 'oldid' );
+               $oldid = $wgRequest->getIntOrNull( 'oldid' );
 
-               if ( isset( $oldid ) ) {
-                       $oldid = intval( $oldid );
-                       if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
-                               $nextid = $this->getTitle()->getNextRevisionID( $oldid );
-                               if ( $nextid ) {
-                                       $oldid = $nextid;
-                               } else {
-                                       $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
-                               }
-                       } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
-                               $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
-                               if ( $previd ) {
-                                       $oldid = $previd;
+               if ( $oldid === null ) {
+                       return 0;
+               }
+
+               if ( $oldid !== 0 ) {
+                       # Load the given revision and check whether the page is another one.
+                       # In that case, update this instance to reflect the change.
+                       $this->mRevision = Revision::newFromId( $oldid );
+                       if ( $this->mRevision !== null ) {
+                               // Revision title doesn't match the page title given?
+                               if ( $this->mPage->getID() != $this->mRevision->getPage() ) {
+                                       $function = array( get_class( $this->mPage ), 'newFromID' );
+                                       $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
                                }
                        }
                }
 
-               if ( !$oldid ) {
-                       $oldid = 0;
+               if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
+                       $nextid = $this->getTitle()->getNextRevisionID( $oldid );
+                       if ( $nextid ) {
+                               $oldid = $nextid;
+                       } else {
+                               $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
+                       }
+               } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
+                       $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
+                       if ( $previd ) {
+                               $oldid = $previd;
+                       }
                }
 
                return $oldid;
@@ -244,31 +253,30 @@ class Article extends Page {
 
        /**
         * Load the revision (including text) into this object
+        *
+        * @deprecated in 1.19; use fetchContent()
         */
        function loadContent() {
-               if ( $this->mContentLoaded ) {
-                       return;
-               }
-
-               wfProfileIn( __METHOD__ );
-
-               $this->fetchContent( $this->getOldID() );
-
-               wfProfileOut( __METHOD__ );
+               $this->fetchContent();
        }
 
        /**
         * Get text of an article from database
         * Does *NOT* follow redirects.
         *
-        * @param $oldid Int: 0 for whatever the latest revision is
         * @return mixed string containing article contents, or false if null
         */
-       function fetchContent( $oldid = 0 ) {
+       function fetchContent() {
                if ( $this->mContentLoaded ) {
                        return $this->mContent;
                }
 
+               wfProfileIn( __METHOD__ );
+
+               $this->mContentLoaded = true;
+
+               $oldid = $this->getOldID();
+
                # Pre-fill content with error message so that if something
                # fails we'll have something telling us what we intended.
                $t = $this->getTitle()->getPrefixedText();
@@ -276,43 +284,39 @@ class Article extends Page {
                $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
 
                if ( $oldid ) {
-                       $revision = Revision::newFromId( $oldid );
-                       if ( !$revision ) {
-                               wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
-                               return false;
-                       }
-                       // Revision title doesn't match the page title given?
-                       if ( $this->mPage->getID() != $revision->getPage() ) {
-                               $function = array( get_class( $this->mPage ), 'newFromID' );
-                               $this->mPage = call_user_func( $function, $revision->getPage() );
-                               if ( !$this->mPage->getId() ) {
-                                       wfDebug( __METHOD__ . " failed to get page data linked to revision id $oldid\n" );
+                       # $this->mRevision might already be fetched by getOldIDFromRequest()
+                       if ( !$this->mRevision ) {
+                               $this->mRevision = Revision::newFromId( $oldid );
+                               if ( !$this->mRevision ) {
+                                       wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
+                                       wfProfileOut( __METHOD__ );
                                        return false;
                                }
                        }
                } else {
                        if ( !$this->mPage->getLatest() ) {
                                wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
+                               wfProfileOut( __METHOD__ );
                                return false;
                        }
 
-                       $revision = $this->mPage->getRevision();
-                       if ( !$revision ) {
+                       $this->mRevision = $this->mPage->getRevision();
+                       if ( !$this->mRevision ) {
                                wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
+                               wfProfileOut( __METHOD__ );
                                return false;
                        }
                }
 
                // @todo 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->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
-
-               $this->mRevIdFetched = $revision->getId();
-               $this->mContentLoaded = true;
-               $this->mRevision =& $revision;
+               $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
+               $this->mRevIdFetched = $this->mRevision->getId();
 
                wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
 
+               wfProfileOut( __METHOD__ );
+
                return $this->mContent;
        }
 
@@ -362,9 +366,20 @@ class Article extends Page {
                wfProfileIn( __METHOD__ );
 
                # Get variables from query string
+               # As side effect this will load the revision and update the title
+               # in a revision ID is passed in the request, so this should remain
+               # the first call of this method even if $oldid is used way below.
                $oldid = $this->getOldID();
 
-               # getOldID may want us to redirect somewhere else
+               # Another whitelist check in case getOldID() is altering the title
+               $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $wgUser );
+               if ( count( $permErrors ) ) {
+                       wfDebug( __METHOD__ . ": denied on secondary read check\n" );
+                       wfProfileOut( __METHOD__ );
+                       throw new PermissionsError( 'read', $permErrors );
+               }
+
+               # getOldID() may as well want us to redirect somewhere else
                if ( $this->mRedirectUrl ) {
                        $wgOut->redirect( $this->mRedirectUrl );
                        wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
@@ -373,9 +388,6 @@ class Article extends Page {
                        return;
                }
 
-               # Set page title (may be overridden by DISPLAYTITLE)
-               $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
-
                # If we got diff in the query, we want to see a diff page instead of the article.
                if ( $wgRequest->getCheck( 'diff' ) ) {
                        wfDebug( __METHOD__ . ": showing diff page\n" );
@@ -385,6 +397,9 @@ class Article extends Page {
                        return;
                }
 
+               # Set page title (may be overridden by DISPLAYTITLE)
+               $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
+
                $wgOut->setArticleFlag( true );
                # Allow frames by default
                $wgOut->allowClickjacking();
@@ -396,7 +411,7 @@ class Article extends Page {
                if ( $wgOut->isPrintable() ) {
                        $parserOptions->setIsPrintable( true );
                        $parserOptions->setEditSection( false );
-               } elseif ( $wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
+               } elseif ( !$this->getTitle()->quickUserCan( 'edit' ) ) {
                        $parserOptions->setEditSection( false );
                }
 
@@ -424,18 +439,14 @@ class Article extends Page {
                        }
                }
 
-               if ( !$wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
-                       $parserOptions->setEditSection( false );
-               }
-
                # Should the parser cache be used?
-               $useParserCache = $this->useParserCache( $oldid );
+               $useParserCache = $this->mPage->isParserCacheUsed( $wgUser, $oldid );
                wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
                if ( $wgUser->getStubThreshold() ) {
                        wfIncrStats( 'pcache_miss_stub' );
                }
 
-               $wasRedirected = $this->showRedirectedFromHeader();
+               $this->showRedirectedFromHeader();
                $this->showNamespaceHeader();
 
                # Iterate through the possible ways of constructing the output text.
@@ -450,12 +461,25 @@ class Article extends Page {
                                        wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
                                        break;
                                case 2:
+                                       # Early abort if the page doesn't exist
+                                       if ( !$this->mPage->exists() ) {
+                                               wfDebug( __METHOD__ . ": showing missing article\n" );
+                                               $this->showMissingArticle();
+                                               wfProfileOut( __METHOD__ );
+                                               return;
+                                       }
+
                                        # Try the parser cache
                                        if ( $useParserCache ) {
                                                $this->mParserOutput = $parserCache->get( $this, $parserOptions );
 
                                                if ( $this->mParserOutput !== false ) {
-                                                       wfDebug( __METHOD__ . ": showing parser cache contents\n" );
+                                                       if ( $oldid ) {
+                                                               wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
+                                                               $this->setOldSubtitle( $oldid );
+                                                       } else {
+                                                               wfDebug( __METHOD__ . ": showing parser cache contents\n" );
+                                                       }
                                                        $wgOut->addParserOutput( $this->mParserOutput );
                                                        # Ensure that UI elements requiring revision ID have
                                                        # the correct version information.
@@ -469,26 +493,11 @@ class Article extends Page {
                                        }
                                        break;
                                case 3:
-                                       $text = $this->getContent();
-                                       if ( $text === false || $this->mPage->getID() == 0 ) {
-                                               wfDebug( __METHOD__ . ": showing missing article\n" );
-                                               $this->showMissingArticle();
-                                               wfProfileOut( __METHOD__ );
-                                               return;
-                                       }
-
-                                       # Another whitelist check in case oldid is altering the title
-                                       if ( !$this->getTitle()->userCanRead() ) {
-                                               wfDebug( __METHOD__ . ": denied on secondary read check\n" );
-                                               $wgOut->loginToUse();
-                                               $wgOut->output();
-                                               $wgOut->disable();
-                                               wfProfileOut( __METHOD__ );
-                                               return;
-                                       }
+                                       # This will set $this->mRevision if needed
+                                       $this->fetchContent();
 
                                        # Are we looking at an old revision
-                                       if ( $oldid && !is_null( $this->mRevision ) ) {
+                                       if ( $oldid && $this->mRevision ) {
                                                $this->setOldSubtitle( $oldid );
 
                                                if ( !$this->showDeletedRevisionHeader() ) {
@@ -496,18 +505,6 @@ class Article extends Page {
                                                        wfProfileOut( __METHOD__ );
                                                        return;
                                                }
-
-                                               # If this "old" version is the current, then try the parser cache...
-                                               if ( $oldid === $this->mPage->getLatest() && $this->useParserCache( false ) ) {
-                                                       $this->mParserOutput = $parserCache->get( $this, $parserOptions );
-                                                       if ( $this->mParserOutput ) {
-                                                               wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
-                                                               $wgOut->addParserOutput( $this->mParserOutput );
-                                                               $wgOut->setRevisionId( $this->mPage->getLatest() );
-                                                               $outputDone = true;
-                                                               break;
-                                                       }
-                                               }
                                        }
 
                                        # Ensure that UI elements requiring revision ID have
@@ -523,12 +520,12 @@ class Article extends Page {
                                                # Allow extensions do their own custom view for certain pages
                                                $outputDone = true;
                                        } else {
+                                               $text = $this->getContent();
                                                $rt = Title::newFromRedirectArray( $text );
                                                if ( $rt ) {
                                                        wfDebug( __METHOD__ . ": showing redirect=no page\n" );
                                                        # Viewing a redirect page (e.g. with parameter redirect=no)
-                                                       # Don't append the subtitle if this was an old revision
-                                                       $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
+                                                       $wgOut->addHTML( $this->viewRedirect( $rt ) );
                                                        # Parse just to get categories, displaytitle, etc.
                                                        $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
                                                        $wgOut->addParserOutputNoText( $this->mParserOutput );
@@ -617,7 +614,7 @@ class Article extends Page {
                $unhide = $wgRequest->getInt( 'unhide' ) == 1;
                $oldid = $this->getOldID();
 
-               $de = new DifferenceEngine( $this->getTitle(), $oldid, $diff, $rcid, $purge, $unhide );
+               $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
                // DifferenceEngine directly fetched the revision:
                $this->mRevIdFetched = $de->mNewid;
                $de->showDiffPage( $diffOnly );
@@ -775,16 +772,14 @@ class Article extends Page {
                        // This is an internally redirected page view.
                        // We'll need a backlink to the source page for navigation.
                        if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
-                               $redir = Linker::link(
+                               $redir = Linker::linkKnown(
                                        $this->mRedirectedFrom,
                                        null,
                                        array(),
-                                       array( 'redirect' => 'no' ),
-                                       array( 'known', 'noclasses' )
+                                       array( 'redirect' => 'no' )
                                );
 
-                               $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
-                               $wgOut->setSubtitle( $s );
+                               $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
 
                                // Set the fragment if one was specified in the redirect
                                if ( strval( $this->getTitle()->getFragment() ) != '' ) {
@@ -804,8 +799,7 @@ class Article extends Page {
                        // If it was reported from a trusted site, supply a backlink.
                        if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
                                $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
-                               $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
-                               $wgOut->setSubtitle( $s );
+                               $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
 
                                return true;
                        }
@@ -1006,8 +1000,145 @@ class Article extends Page {
                }
        }
 
+       /**
+        * Generate the navigation links when browsing through an article revisions
+        * It shows the information as:
+        *   Revision as of \<date\>; view current revision
+        *   \<- Previous version | Next Version -\>
+        *
+        * @param $oldid String: revision ID of this article revision
+        */
+       public function setOldSubtitle( $oldid = 0 ) {
+               global $wgLang, $wgOut, $wgUser, $wgRequest;
+
+               if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
+                       return;
+               }
+
+               $unhide = $wgRequest->getInt( 'unhide' ) == 1;
+
+               # Cascade unhide param in links for easy deletion browsing
+               $extraParams = array();
+               if ( $wgRequest->getVal( 'unhide' ) ) {
+                       $extraParams['unhide'] = 1;
+               }
+
+               $revision = Revision::newFromId( $oldid );
+               $timestamp = $revision->getTimestamp();
+
+               $current = ( $oldid == $this->mPage->getLatest() );
+               $td = $wgLang->timeanddate( $timestamp, true );
+               $tddate = $wgLang->date( $timestamp, true );
+               $tdtime = $wgLang->time( $timestamp, true );
+
+               # Show user links if allowed to see them. If hidden, then show them only if requested...
+               $userlinks = Linker::revUserTools( $revision, !$unhide );
+
+               $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
+                       ? 'revision-info-current'
+                       : 'revision-info';
+
+               $wgOut->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg,
+                       $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate,
+                       $tdtime, $revision->getUser() )->parse() . "</div>" );
+
+               $lnk = $current
+                       ? wfMsgHtml( 'currentrevisionlink' )
+                       : Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'currentrevisionlink' ),
+                               array(),
+                               $extraParams,
+                               array( 'known', 'noclasses' )
+                       );
+               $curdiff = $current
+                       ? wfMsgHtml( 'diff' )
+                       : Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'diff' ),
+                               array(),
+                               array(
+                                       'diff' => 'cur',
+                                       'oldid' => $oldid
+                               ) + $extraParams,
+                               array( 'known', 'noclasses' )
+                       );
+               $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ;
+               $prevlink = $prev
+                       ? Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'previousrevision' ),
+                               array(),
+                               array(
+                                       'direction' => 'prev',
+                                       'oldid' => $oldid
+                               ) + $extraParams,
+                               array( 'known', 'noclasses' )
+                       )
+                       : wfMsgHtml( 'previousrevision' );
+               $prevdiff = $prev
+                       ? Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'diff' ),
+                               array(),
+                               array(
+                                       'diff' => 'prev',
+                                       'oldid' => $oldid
+                               ) + $extraParams,
+                               array( 'known', 'noclasses' )
+                       )
+                       : wfMsgHtml( 'diff' );
+               $nextlink = $current
+                       ? wfMsgHtml( 'nextrevision' )
+                       : Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'nextrevision' ),
+                               array(),
+                               array(
+                                       'direction' => 'next',
+                                       'oldid' => $oldid
+                               ) + $extraParams,
+                               array( 'known', 'noclasses' )
+                       );
+               $nextdiff = $current
+                       ? wfMsgHtml( 'diff' )
+                       : Linker::link(
+                               $this->getTitle(),
+                               wfMsgHtml( 'diff' ),
+                               array(),
+                               array(
+                                       'diff' => 'next',
+                                       'oldid' => $oldid
+                               ) + $extraParams,
+                               array( 'known', 'noclasses' )
+                       );
+
+               $cdel = '';
+
+               // User can delete revisions or view deleted revisions...
+               $canHide = $wgUser->isAllowed( 'deleterevision' );
+               if ( $canHide || ( $revision->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) {
+                       if ( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
+                               $cdel = Linker::revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops
+                       } else {
+                               $query = array(
+                                       'type'   => 'revision',
+                                       'target' => $this->getTitle()->getPrefixedDbkey(),
+                                       'ids'    => $oldid
+                               );
+                               $cdel = Linker::revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide );
+                       }
+                       $cdel .= ' ';
+               }
+
+               $wgOut->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
+                       wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
+                       $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>" );
+       }
+
        /**
         * Execute the uncached parse for action=view
+        * @return bool
         */
        public function doViewParse() {
                global $wgOut;
@@ -1089,6 +1220,10 @@ class Article extends Page {
                }
 
                // the loop prepends the arrow image before the link, so the first case needs to be outside
+
+               /**
+                * @var $title Title
+                */
                $title = array_shift( $target );
 
                if ( $forceKnown ) {
@@ -1155,18 +1290,9 @@ class Article extends Page {
                $wgOut->wrapWikiMsg( "<div id='mw_trackbacks'>\n$1\n</div>\n", array( 'trackbackbox', $tbtext ) );
        }
 
-       /**
-        * Removes trackback record for current article from trackbacks table
-        * @deprecated since 1.18
-        */
-       public function deletetrackback() {
-               return Action::factory( 'deletetrackback', $this )->show();
-       }
-
        /**
         * Handle action=render
         */
-
        public function render() {
                global $wgOut;
 
@@ -1175,185 +1301,56 @@ class Article extends Page {
        }
 
        /**
-        * Handle action=purge
+        * action=protect handler
         */
-       public function purge() {
-               return Action::factory( 'purge', $this )->show();
+       public function protect() {
+               $form = new ProtectionForm( $this );
+               $form->execute();
        }
 
        /**
-        * Mark this particular edit/page as patrolled
-        * @deprecated since 1.18
+        * action=unprotect handler (alias)
         */
-       public function markpatrolled() {
-               Action::factory( 'markpatrolled', $this )->show();
+       public function unprotect() {
+               $this->protect();
        }
 
        /**
-        * User-interface handler for the "watch" action.
-        * Requires Request to pass a token as of 1.18.
-        * @deprecated since 1.18
+        * UI entry point for page deletion
         */
-       public function watch() {
-               Action::factory( 'watch', $this )->show();
-       }
+       public function delete() {
+               global $wgOut, $wgRequest, $wgLang;
 
-       /**
-        * Add this page to $wgUser's watchlist
-        *
-        * This is safe to be called multiple times
-        *
-        * @return bool true on successful watch operation
-        * @deprecated since 1.18
-        */
-       public function doWatch() {
-               global $wgUser;
-               return WatchAction::doWatch( $this->getTitle(), $wgUser );
-       }
+               # This code desperately needs to be totally rewritten
 
-       /**
-        * User interface handler for the "unwatch" action.
-        * Requires Request to pass a token as of 1.18.
-        * @deprecated since 1.18
-        */
-       public function unwatch() {
-               Action::factory( 'unwatch', $this )->show();
-       }
+               $title = $this->getTitle();
+               $user = $this->getContext()->getUser();
 
-       /**
-        * Stop watching a page
-        * @return bool true on successful unwatch
-        * @deprecated since 1.18
-        */
-       public function doUnwatch() {
-               global $wgUser;
-               return WatchAction::doUnwatch( $this->getTitle(), $wgUser );
-       }
-
-       /**
-        * action=protect handler
-        */
-       public function protect() {
-               $form = new ProtectionForm( $this );
-               $form->execute();
-       }
-
-       /**
-        * action=unprotect handler (alias)
-        */
-       public function unprotect() {
-               $this->protect();
-       }
-
-       /**
-        * Info about this page
-        * Called for ?action=info when $wgAllowPageInfo is on.
-        */
-       public function info() {
-               Action::factory( 'info', $this )->show();
-       }
-
-       /**
-        * Overriden by ImagePage class, only present here to avoid a fatal error
-        * Called for ?action=revert
-        */
-       public function revert() {
-               Action::factory( 'revert', $this )->show();
-       }
-
-       /**
-        * User interface for rollback operations
-        */
-       public function rollback() {
-               Action::factory( 'rollback', $this )->show();
-       }
-
-       /**
-        * Output a redirect back to the article.
-        * This is typically used after an edit.
-        *
-        * @deprecated in 1.18; call $wgOut->redirect() directly
-        * @param $noRedir Boolean: add redirect=no
-        * @param $sectionAnchor String: section to redirect to, including "#"
-        * @param $extraQuery String: extra query params
-        */
-       public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
-               wfDeprecated( __METHOD__ );
-               global $wgOut;
-
-               if ( $noRedir ) {
-                       $query = 'redirect=no';
-                       if ( $extraQuery )
-                               $query .= "&$extraQuery";
-               } else {
-                       $query = $extraQuery;
-               }
-
-               $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor );
-       }
-
-       /**
-        * UI entry point for page deletion
-        */
-       public function delete() {
-               global $wgOut, $wgRequest;
-
-               $confirm = $wgRequest->wasPosted() &&
-                               $this->getContext()->getUser()->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 .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason;
-               } elseif ( $reason == 'other' ) {
-                       $reason = $this->DeleteReason;
-               }
-
-               # Flag to hide all contents of the archived revisions
-               $suppress = $wgRequest->getVal( 'wpSuppress' ) && $this->getContext()->getUser()->isAllowed( 'suppressrevision' );
-
-               # This code desperately needs to be totally rewritten
+               # Check permissions
+               $permission_errors = $title->getUserPermissionsErrors( 'delete', $user );
+               if ( count( $permission_errors ) ) {
+                       throw new PermissionsError( 'delete', $permission_errors );
+               }
 
                # Read-only check...
                if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-
-                       return;
+                       throw new ReadOnlyError;
                }
 
-               # Check permissions
-               $permission_errors = $this->getTitle()->getUserPermissionsErrors( 'delete', $this->getContext()->getUser() );
-
-               if ( count( $permission_errors ) > 0 ) {
-                       $wgOut->showPermissionsErrorPage( $permission_errors );
-
-                       return;
-               }
-
-               $wgOut->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
-
                # Better double-check that it hasn't been deleted yet!
                $dbw = wfGetDB( DB_MASTER );
-               $conds = $this->getTitle()->pageCond();
+               $conds = $title->pageCond();
                $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
                if ( $latest === false ) {
-                       $wgOut->showFatalError(
-                               Html::rawElement(
-                                       'div',
-                                       array( 'class' => 'error mw-error-cannotdelete' ),
-                                       wfMsgExt( 'cannotdelete', array( 'parse' ),
-                                               wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
-                               )
-                       );
+                       $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) );
+                       $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
+                                       array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) )
+                               );
                        $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
                        LogEventsList::showLogExtract(
                                $wgOut,
                                'delete',
-                               $this->getTitle()->getPrefixedText()
+                               $title->getPrefixedText()
                        );
 
                        return;
@@ -1361,21 +1358,39 @@ class Article extends Page {
 
                # Hack for big sites
                $bigHistory = $this->mPage->isBigDeletion();
-               if ( $bigHistory && !$this->getTitle()->userCan( 'bigdelete' ) ) {
-                       global $wgLang, $wgDeleteRevisionsLimit;
+               if ( $bigHistory && !$title->userCan( 'bigdelete' ) ) {
+                       global $wgDeleteRevisionsLimit;
 
+                       $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) );
                        $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
                                array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
 
                        return;
                }
 
-               if ( $confirm ) {
+               $deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
+               $deleteReason = $wgRequest->getText( 'wpReason' );
+
+               if ( $deleteReasonList == 'other' ) {
+                       $reason = $deleteReason;
+               } elseif ( $deleteReason != '' ) {
+                       // Entry from drop down menu + additional comment
+                       $reason = $deleteReasonList . wfMsgForContent( 'colon-separator' ) . $deleteReason;
+               } else {
+                       $reason = $deleteReasonList;
+               }
+
+               if ( $wgRequest->wasPosted() && $user->matchEditToken( $wgRequest->getVal( 'wpEditToken' ),
+                       array( 'delete', $this->getTitle()->getPrefixedText() ) ) )
+               {
+                       # Flag to hide all contents of the archived revisions
+                       $suppress = $wgRequest->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
+
                        $this->doDelete( $reason, $suppress );
 
-                       if ( $wgRequest->getCheck( 'wpWatch' ) && $this->getContext()->getUser()->isLoggedIn() ) {
+                       if ( $wgRequest->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) {
                                $this->doWatch();
-                       } elseif ( $this->getTitle()->userIsWatching() ) {
+                       } elseif ( $title->userIsWatching() ) {
                                $this->doUnwatch();
                        }
 
@@ -1389,14 +1404,12 @@ class Article extends Page {
                }
 
                // If the page has a history, insert a warning
-               if ( $hasHistory && !$confirm ) {
-                       global $wgLang;
-
+               if ( $hasHistory ) {
                        $revisions = $this->mPage->estimateRevisionCount();
                        // @todo FIXME: i18n issue/patchwork message
                        $wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' .
                                wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) .
-                               wfMsgHtml( 'word-separator' ) . Linker::link( $this->getTitle(),
+                               wfMsgHtml( 'word-separator' ) . Linker::link( $title,
                                        wfMsgHtml( 'history' ),
                                        array( 'rel' => 'archives' ),
                                        array( 'action' => 'history' ) ) .
@@ -1423,14 +1436,16 @@ class Article extends Page {
 
                wfDebug( "Article::confirmDelete\n" );
 
-               $deleteBackLink = Linker::linkKnown( $this->getTitle() );
-               $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) );
+               $wgOut->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
+               $wgOut->addBacklinkSubtitle( $this->getTitle() );
                $wgOut->setRobotPolicy( 'noindex,nofollow' );
                $wgOut->addWikiMsg( 'confirmdeletetext' );
 
                wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) );
 
-               if ( $this->getContext()->getUser()->isAllowed( 'suppressrevision' ) ) {
+               $user = $this->getContext()->getUser();
+
+               if ( $user->isAllowed( 'suppressrevision' ) ) {
                        $suppress = "<tr id=\"wpDeleteSuppressRow\">
                                        <td></td>
                                        <td class='mw-input'><strong>" .
@@ -1441,7 +1456,7 @@ class Article extends Page {
                } else {
                        $suppress = '';
                }
-               $checkWatch = $this->getContext()->getUser()->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching();
+               $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching();
 
                $form = Xml::openElement( 'form', array( 'method' => 'post',
                        'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
@@ -1474,7 +1489,7 @@ class Article extends Page {
                        </tr>";
 
                # Disallow watching if user is not logged in
-               if ( $this->getContext()->getUser()->isLoggedIn() ) {
+               if ( $user->isLoggedIn() ) {
                        $form .= "
                        <tr>
                                <td></td>
@@ -1496,10 +1511,10 @@ class Article extends Page {
                        </tr>" .
                        Xml::closeElement( 'table' ) .
                        Xml::closeElement( 'fieldset' ) .
-                       Html::hidden( 'wpEditToken', $this->getContext()->getUser()->editToken() ) .
+                       Html::hidden( 'wpEditToken', $user->editToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) .
                        Xml::closeElement( 'form' );
 
-                       if ( $this->getContext()->getUser()->isAllowed( 'editinterface' ) ) {
+                       if ( $user->isAllowed( 'editinterface' ) ) {
                                $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
                                $link = Linker::link(
                                        $title,
@@ -1519,6 +1534,8 @@ class Article extends Page {
 
        /**
         * Perform a deletion and output success or failure messages
+        * @param $reason
+        * @param $suppress bool
         */
        public function doDelete( $reason, $suppress = false ) {
                global $wgOut;
@@ -1526,7 +1543,7 @@ class Article extends Page {
                $id = $this->getTitle()->getArticleID( Title::GAID_FOR_UPDATE );
 
                $error = '';
-               if ( $this->mPage->doDeleteArticle( $reason, $suppress, $id, $error ) ) {
+               if ( $this->mPage->doDeleteArticle( $reason, $suppress, $id, true, $error ) ) {
                        $deleted = $this->getTitle()->getPrefixedText();
 
                        $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) );
@@ -1537,16 +1554,11 @@ class Article extends Page {
                        $wgOut->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
                        $wgOut->returnToMain( false );
                } else {
+                       $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) );
                        if ( $error == '' ) {
-                               $wgOut->showFatalError(
-                                       Html::rawElement(
-                                               'div',
-                                               array( 'class' => 'error mw-error-cannotdelete' ),
-                                               wfMsgExt( 'cannotdelete', array( 'parse' ),
-                                                       wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
-                                       )
+                               $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
+                                       array( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
                                );
-
                                $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
 
                                LogEventsList::showLogExtract(
@@ -1555,156 +1567,11 @@ class Article extends Page {
                                        $this->getTitle()->getPrefixedText()
                                );
                        } else {
-                               $wgOut->showFatalError( $error );
+                               $wgOut->addHTML( $error );
                        }
                }
        }
 
-       /**
-        * Generate the navigation links when browsing through an article revisions
-        * It shows the information as:
-        *   Revision as of \<date\>; view current revision
-        *   \<- Previous version | Next Version -\>
-        *
-        * @param $oldid String: revision ID of this article revision
-        */
-       public function setOldSubtitle( $oldid = 0 ) {
-               global $wgLang, $wgOut, $wgUser, $wgRequest;
-
-               if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
-                       return;
-               }
-
-               $unhide = $wgRequest->getInt( 'unhide' ) == 1;
-
-               # Cascade unhide param in links for easy deletion browsing
-               $extraParams = array();
-               if ( $wgRequest->getVal( 'unhide' ) ) {
-                       $extraParams['unhide'] = 1;
-               }
-
-               $revision = Revision::newFromId( $oldid );
-               $timestamp = $revision->getTimestamp();
-
-               $current = ( $oldid == $this->mPage->getLatest() );
-               $td = $wgLang->timeanddate( $timestamp, true );
-               $tddate = $wgLang->date( $timestamp, true );
-               $tdtime = $wgLang->time( $timestamp, true );
-
-               $lnk = $current
-                       ? wfMsgHtml( 'currentrevisionlink' )
-                       : Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'currentrevisionlink' ),
-                               array(),
-                               $extraParams,
-                               array( 'known', 'noclasses' )
-                       );
-               $curdiff = $current
-                       ? wfMsgHtml( 'diff' )
-                       : Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'diff' ),
-                               array(),
-                               array(
-                                       'diff' => 'cur',
-                                       'oldid' => $oldid
-                               ) + $extraParams,
-                               array( 'known', 'noclasses' )
-                       );
-               $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ;
-               $prevlink = $prev
-                       ? Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'previousrevision' ),
-                               array(),
-                               array(
-                                       'direction' => 'prev',
-                                       'oldid' => $oldid
-                               ) + $extraParams,
-                               array( 'known', 'noclasses' )
-                       )
-                       : wfMsgHtml( 'previousrevision' );
-               $prevdiff = $prev
-                       ? Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'diff' ),
-                               array(),
-                               array(
-                                       'diff' => 'prev',
-                                       'oldid' => $oldid
-                               ) + $extraParams,
-                               array( 'known', 'noclasses' )
-                       )
-                       : wfMsgHtml( 'diff' );
-               $nextlink = $current
-                       ? wfMsgHtml( 'nextrevision' )
-                       : Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'nextrevision' ),
-                               array(),
-                               array(
-                                       'direction' => 'next',
-                                       'oldid' => $oldid
-                               ) + $extraParams,
-                               array( 'known', 'noclasses' )
-                       );
-               $nextdiff = $current
-                       ? wfMsgHtml( 'diff' )
-                       : Linker::link(
-                               $this->getTitle(),
-                               wfMsgHtml( 'diff' ),
-                               array(),
-                               array(
-                                       'diff' => 'next',
-                                       'oldid' => $oldid
-                               ) + $extraParams,
-                               array( 'known', 'noclasses' )
-                       );
-
-               $cdel = '';
-
-               // User can delete revisions or view deleted revisions...
-               $canHide = $wgUser->isAllowed( 'deleterevision' );
-               if ( $canHide || ( $revision->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) {
-                       if ( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
-                               $cdel = Linker::revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops
-                       } else {
-                               $query = array(
-                                       'type'   => 'revision',
-                                       'target' => $this->getTitle()->getPrefixedDbkey(),
-                                       'ids'    => $oldid
-                               );
-                               $cdel = Linker::revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide );
-                       }
-                       $cdel .= ' ';
-               }
-
-               # Show user links if allowed to see them. If hidden, then show them only if requested...
-               $userlinks = Linker::revUserTools( $revision, !$unhide );
-
-               $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
-                       ? 'revision-info-current'
-                       : 'revision-info';
-
-               $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" .
-                       wfMsgExt(
-                               $infomsg,
-                               array( 'parseinline', 'replaceafter' ),
-                               $td,
-                               $userlinks,
-                               $revision->getID(),
-                               $tddate,
-                               $tdtime,
-                               $revision->getUser()
-                       ) .
-                       "</div>\n" .
-                       "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
-                       $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
-
-               $wgOut->setSubtitle( $r );
-       }
-
        /* Caching functions */
 
        /**
@@ -1919,6 +1786,119 @@ class Article extends Page {
                }
        }
 
+       /**
+        * Removes trackback record for current article from trackbacks table
+        * @deprecated since 1.18
+        */
+       public function deletetrackback() {
+               return Action::factory( 'deletetrackback', $this )->show();
+       }
+
+       /**
+        * Info about this page
+        * @deprecated since 1.19
+        */
+       public function info() {
+               Action::factory( 'info', $this )->show();
+       }
+
+       /**
+        * Mark this particular edit/page as patrolled
+        * @deprecated since 1.18
+        */
+       public function markpatrolled() {
+               Action::factory( 'markpatrolled', $this )->show();
+       }
+
+       /**
+        * Handle action=purge
+        * @deprecated since 1.19
+        */
+       public function purge() {
+               return Action::factory( 'purge', $this )->show();
+       }
+
+       /**
+        * Handle action=revert
+        * @deprecated since 1.19
+        */
+       public function revert() {
+               Action::factory( 'revert', $this )->show();
+       }
+
+       /**
+        * Handle action=rollback
+        * @deprecated since 1.19
+        */
+       public function rollback() {
+               Action::factory( 'rollback', $this )->show();
+       }
+
+       /**
+        * User-interface handler for the "watch" action.
+        * Requires Request to pass a token as of 1.18.
+        * @deprecated since 1.18
+        */
+       public function watch() {
+               Action::factory( 'watch', $this )->show();
+       }
+
+       /**
+        * Add this page to $wgUser's watchlist
+        *
+        * This is safe to be called multiple times
+        *
+        * @return bool true on successful watch operation
+        * @deprecated since 1.18
+        */
+       public function doWatch() {
+               global $wgUser;
+               return WatchAction::doWatch( $this->getTitle(), $wgUser );
+       }
+
+       /**
+        * User interface handler for the "unwatch" action.
+        * Requires Request to pass a token as of 1.18.
+        * @deprecated since 1.18
+        */
+       public function unwatch() {
+               Action::factory( 'unwatch', $this )->show();
+       }
+
+       /**
+        * Stop watching a page
+        * @return bool true on successful unwatch
+        * @deprecated since 1.18
+        */
+       public function doUnwatch() {
+               global $wgUser;
+               return WatchAction::doUnwatch( $this->getTitle(), $wgUser );
+       }
+
+       /**
+        * Output a redirect back to the article.
+        * This is typically used after an edit.
+        *
+        * @deprecated in 1.18; call $wgOut->redirect() directly
+        * @param $noRedir Boolean: add redirect=no
+        * @param $sectionAnchor String: section to redirect to, including "#"
+        * @param $extraQuery String: extra query params
+        */
+       public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
+               wfDeprecated( __METHOD__ );
+               global $wgOut;
+
+               if ( $noRedir ) {
+                       $query = 'redirect=no';
+                       if ( $extraQuery )
+                               $query .= "&$extraQuery";
+               } else {
+                       $query = $extraQuery;
+               }
+
+               $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor );
+       }
+
        /**
         * Use PHP's magic __get handler to handle accessing of
         * raw WikiPage fields for backwards compatibility.
@@ -2042,21 +2022,21 @@ class Article extends Page {
         * @param $title Title
         */
        public static function onArticleCreate( $title ) {
-               return WikiPage::onArticleCreate( $title );
+               WikiPage::onArticleCreate( $title );
        }
 
        /**
         * @param $title Title
         */
        public static function onArticleDelete( $title ) {
-               return WikiPage::onArticleDelete( $title );
+               WikiPage::onArticleDelete( $title );
        }
 
        /**
         * @param $title Title
         */
        public static function onArticleEdit( $title ) {
-               return WikiPage::onArticleEdit( $title );
+               WikiPage::onArticleEdit( $title );
        }
 
        /**
@@ -2085,10 +2065,16 @@ class PoolWorkArticleView extends PoolCounterWork {
                $this->parserOptions = $parserOptions;
        }
 
+       /**
+        * @return bool
+        */
        function doWork() {
                return $this->mArticle->doViewParse();
        }
 
+       /**
+        * @return bool
+        */
        function getCachedWork() {
                global $wgOut;
 
@@ -2106,6 +2092,9 @@ class PoolWorkArticleView extends PoolCounterWork {
                return false;
        }
 
+       /**
+        * @return bool
+        */
        function fallback() {
                return $this->mArticle->tryDirtyCache();
        }