* API: (bug 20700) Add amprop=default to list default messages in list=allmessages...
[lhc/web/wiklou.git] / includes / Article.php
index f1d024e..846061a 100644 (file)
@@ -41,6 +41,7 @@ class Article {
        var $mUser = -1;                  //!< Not loaded
        var $mUserText = '';              //!<
        var $mParserOptions;              //!<
+       var $mParserOutput;               //!<
        /**@}}*/
 
        /**
@@ -406,15 +407,15 @@ class Article {
                if( $data ) {
                        $lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect );
 
-                       $this->mTitle->mArticleID = $data->page_id;
+                       $this->mTitle->mArticleID = intval( $data->page_id );
 
                        # Old-fashioned restrictions
                        $this->mTitle->loadRestrictions( $data->page_restrictions );
 
-                       $this->mCounter     = $data->page_counter;
+                       $this->mCounter     = intval( $data->page_counter );
                        $this->mTouched     = wfTimestamp( TS_MW, $data->page_touched );
-                       $this->mIsRedirect  = $data->page_is_redirect;
-                       $this->mLatest      = $data->page_latest;
+                       $this->mIsRedirect  = intval( $data->page_is_redirect );
+                       $this->mLatest      = intval( $data->page_latest );
                } else {
                        if( is_object( $this->mTitle ) ) {
                                $lc->addBadLinkObj( $this->mTitle );
@@ -725,7 +726,7 @@ class Article {
        public function view() {
                global $wgUser, $wgOut, $wgRequest, $wgContLang;
                global $wgEnableParserCache, $wgStylePath, $wgParser;
-               global $wgUseTrackbacks;
+               global $wgUseTrackbacks, $wgUseFileCache;
 
                wfProfileIn( __METHOD__ );
 
@@ -751,7 +752,7 @@ class Article {
                                wfProfileOut( __METHOD__ );
                                return;
                        # Try file cache
-                       } else if( $this->tryFileCache() ) {
+                       } else if( $wgUseFileCache && $this->tryFileCache() ) {
                                wfDebug( __METHOD__.": done file cache\n" );
                                # tell wgOut that output is taken care of
                                $wgOut->disable();
@@ -772,7 +773,6 @@ class Article {
                }
 
                $wgOut->setArticleFlag( true );
-               $wgOut->setRobotPolicy( $this->getRobotPolicyForView() );
                # Set page title (may be overridden by DISPLAYTITLE)
                $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
 
@@ -794,117 +794,132 @@ class Article {
                # For the main page, overwrite the <title> element with the con-
                # tents of 'pagetitle-view-mainpage' instead of the default (if
                # that's not empty).
-               if( $this->mTitle->equals( Title::newMainPage() ) 
-                       && wfMsgForContent( 'pagetitle-view-mainpage' ) !== '' ) 
+               if( $this->mTitle->equals( Title::newMainPage() )
+                       && ($m=wfMsgForContent( 'pagetitle-view-mainpage' )) !== '' )
                {
-                       $wgOut->setHTMLTitle( wfMsgForContent( 'pagetitle-view-mainpage' ) );
+                       $wgOut->setHTMLTitle( $m );
                }
 
                $wasRedirected = $this->showRedirectedFromHeader();
                $this->showNamespaceHeader();
 
+               # Iterate through the possible ways of constructing the output text.
+               # Keep going until $outputDone is set, or we run out of things to do.
+               $pass = 0;
                $outputDone = false;
-               wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
-
-               # Try the parser cache
-               if( !$outputDone && $useParserCache ) {
-                       $parserOutput = $parserCache->get( $this, $parserOptions );
-                       if ( $parserOutput !== false ) {
-                               wfDebug( __METHOD__.": showing parser cache contents\n" );
-                               $wgOut->addParserOutput( $parserOutput );
-                               // Ensure that UI elements requiring revision ID have
-                               // the correct version information.
-                               $wgOut->setRevisionId( $this->mLatest );
-                               $outputDone = true;
-                       }
-               }
-
-               if ( $outputDone ) {
-                       $this->showViewFooter();
-                       $this->viewUpdates();
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
+               while( !$outputDone && ++$pass ){
+                       switch( $pass ){
+                               case 1:
+                                       wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
+                                       break;
+
+                               case 2:
+                                       # Try the parser cache
+                                       if( $useParserCache ) {
+                                               $this->mParserOutput = $parserCache->get( $this, $parserOptions );
+                                               if ( $this->mParserOutput !== false ) {
+                                                       wfDebug( __METHOD__.": showing parser cache contents\n" );
+                                                       $wgOut->addParserOutput( $this->mParserOutput );
+                                                       # Ensure that UI elements requiring revision ID have
+                                                       # the correct version information.
+                                                       $wgOut->setRevisionId( $this->mLatest );
+                                                       $outputDone = true;
+                                               }
+                                       }
+                                       break;
+
+                               case 3:
+                                       $text = $this->getContent();
+                                       if( $text === false || $this->getID() == 0 ) {
+                                               wfDebug( __METHOD__.": showing missing article\n" );
+                                               $this->showMissingArticle();
+                                               wfProfileOut( __METHOD__ );
+                                               return;
+                                       }
 
-               $text = $this->getContent();
-               if( $text === false || $this->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->mTitle->userCanRead() ) {
+                                               wfDebug( __METHOD__.": denied on secondary read check\n" );
+                                               $wgOut->loginToUse();
+                                               $wgOut->output();
+                                               $wgOut->disable();
+                                               wfProfileOut( __METHOD__ );
+                                               return;
+                                       }
 
-               # Another whitelist check in case oldid is altering the title
-               if( !$this->mTitle->userCanRead() ) {
-                       wfDebug( __METHOD__.": denied on secondary read check\n" );
-                       $wgOut->loginToUse();
-                       $wgOut->output();
-                       $wgOut->disable();
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
+                                       # Are we looking at an old revision
+                                       if( $oldid && !is_null( $this->mRevision ) ) {
+                                               $this->setOldSubtitle( $oldid );
+                                               if ( !$this->showDeletedRevisionHeader() ) {
+                                                       wfDebug( __METHOD__.": cannot view deleted revision\n" );
+                                                       wfProfileOut( __METHOD__ );
+                                                       return;
+                                               }
+                                               # If this "old" version is the current, then try the parser cache...
+                                               if ( $oldid === $this->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->mLatest );
+                                                               $this->showViewFooter();
+                                                               $this->viewUpdates();
+                                                               wfProfileOut( __METHOD__ );
+                                                               return;
+                                                       }
+                                               }
+                                       }
 
-               # We're looking at an old revision
-               if( $oldid && !is_null( $this->mRevision ) ) {
-                       $this->setOldSubtitle( $oldid );
-                       if ( !$this->showDeletedRevisionHeader() ) {
-                               wfDebug( __METHOD__.": cannot view deleted revision\n" );
-                               wfProfileOut( __METHOD__ );
-                               return;
-                       }
+                                       # Ensure that UI elements requiring revision ID have
+                                       # the correct version information.
+                                       $wgOut->setRevisionId( $this->getRevIdFetched() );
+
+                                       # Pages containing custom CSS or JavaScript get special treatment
+                                       if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+                                               wfDebug( __METHOD__.": showing CSS/JS source\n" );
+                                               $this->showCssOrJsPage();
+                                               $outputDone = true;
+                                       } else if( $rt = Title::newFromRedirectArray( $text ) ) {
+                                               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() ) );
+                                               # Parse just to get categories, displaytitle, etc.
+                                               $this->mParserOutput = $wgParser->parse( $text, $this->mTitle, $parserOptions );
+                                               $wgOut->addParserOutputNoText( $this->mParserOutput );
+                                               $outputDone = true;
+                                       }
+                                       break;
+
+                               case 4:
+                                       # Run the parse, protected by a pool counter
+                                       wfDebug( __METHOD__.": doing uncached parse\n" );
+                                       $key = $parserCache->getKey( $this, $parserOptions );
+                                       $poolCounter = PoolCounter::factory( 'Article::view', $key );
+                                       $dirtyCallback = $useParserCache ? array( $this, 'tryDirtyCache' ) : false;
+                                       $status = $poolCounter->executeProtected( array( $this, 'doViewParse' ), $dirtyCallback );
+
+                                       if ( !$status->isOK() ) {
+                                               # Connection or timeout error
+                                               $this->showPoolError( $status );
+                                               wfProfileOut( __METHOD__ );
+                                               return;
+                                       } else {
+                                               $outputDone = true;
+                                       }
+                                       break;
 
-                       if ( $oldid === $this->getLatest() && $this->useParserCache( false ) ) {
-                               $parserOutput = $parserCache->get( $this, $parserOptions );
-                               if ( $parserOutput ) {
-                                       wfDebug( __METHOD__.": showing parser cache for current rev permalink\n" );
-                                       $wgOut->addParserOutput( $parserOutput );
-                                       $this->showViewFooter();
-                                       $this->viewUpdates();
-                                       wfProfileOut( __METHOD__ );
-                                       return;
-                               }
+                               # Should be unreachable, but just in case...
+                               default:
+                                       break 2;
                        }
                }
 
-               // Ensure that UI elements requiring revision ID have
-               // the correct version information.
-               $wgOut->setRevisionId( $this->getRevIdFetched() );
-
-               // Pages containing custom CSS or JavaScript get special treatment
-               if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
-                       wfDebug( __METHOD__.": showing CSS/JS source\n" );
-                       $this->showCssOrJsPage();
-                       $outputDone = true;
-               } else if( $rt = Title::newFromRedirectArray( $text ) ) {
-                       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() ) );
-                       # Parse just to get categories, displaytitle, etc.
-                       $parserOutput = $wgParser->parse( $text, $this->mTitle, $parserOptions );
-                       $wgOut->addParserOutputNoText( $parserOutput );
-                       $outputDone = true;
-               }
-               if ( $outputDone ) {
-                       $this->showViewFooter();
-                       $this->viewUpdates();
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
-
-               # Run the parse, protected by a pool counter
-               wfDebug( __METHOD__.": doing uncached parse\n" );
-               $key = $parserCache->getKey( $this, $parserOptions );
-               $poolCounter = PoolCounter::factory( 'Article::view', $key );
-               $dirtyCallback = $useParserCache ? array( $this, 'tryDirtyCache' ) : false;
-               $status = $poolCounter->executeProtected( array( $this, 'doViewParse' ), $dirtyCallback );
-
-               if ( !$status->isOK() ) {
-                       # Connection or timeout error
-                       $this->showPoolError( $status );
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
+               # Now that we've filled $this->mParserOutput, we know whether
+               # there are any __NOINDEX__ tags on the page
+               $policy = $this->getRobotPolicy( 'view' );
+               $wgOut->setIndexPolicy( $policy['index'] );
+               $wgOut->setFollowPolicy( $policy['follow'] );
 
                $this->showViewFooter();
                $this->viewUpdates();
@@ -922,7 +937,7 @@ class Article {
                $rcid = $wgRequest->getVal( 'rcid' );
                $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
                $purge = $wgRequest->getVal( 'action' ) == 'purge';
-               $htmldiff = $wgRequest->getVal( 'htmldiff' , false);
+               $htmldiff = $wgRequest->getBool( 'htmldiff' );
                $unhide = $wgRequest->getInt('unhide') == 1;
                $oldid = $this->getOldID();
 
@@ -962,8 +977,24 @@ class Article {
 
        /**
         * Get the robot policy to be used for the current action=view request.
+        * @return String the policy that should be set
+        * @deprecated use getRobotPolicy() instead, which returns an associative
+        *    array
         */
        public function getRobotPolicyForView() {
+               wfDeprecated( __FUNC__ );
+               $policy = $this->getRobotPolicy( 'view' );
+               return $policy['index'] . ',' . $policy['follow'];
+       }
+       
+       /**
+        * Get the robot policy to be used for the current view
+        * @param $action String the action= GET parameter
+        * @return Array the policy that should be set
+        * TODO: actions other than 'view'
+        */
+       public function getRobotPolicy( $action ){
+               
                global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies;
                global $wgDefaultRobotPolicy, $wgRequest;
 
@@ -973,27 +1004,77 @@ class Article {
                        if( !$this->mTitle->isSubpage() ) {
                                $block = new Block();
                                if( $block->load( $this->mTitle->getText() ) ) {
-                                       return 'noindex,nofollow';
+                                       return array( 'index'  => 'noindex',
+                                                     'follow' => 'nofollow' );
                                }
                        }
                }
 
                if( $this->getID() === 0 || $this->getOldID() ) {
-                       return 'noindex,nofollow';
+                       # Non-articles (special pages etc), and old revisions
+                       return array( 'index'  => 'noindex',
+                                     'follow' => 'nofollow' );
                } elseif( $wgOut->isPrintable() ) {
                        # Discourage indexing of printable versions, but encourage following
-                       return 'noindex,follow';
+                       return array( 'index'  => 'noindex',
+                                     'follow' => 'follow' );
                } elseif( $wgRequest->getInt('curid') ) {
                        # For ?curid=x urls, disallow indexing
-                       return 'noindex,follow';
-               } elseif( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
-                       return $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
-               } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+                       return array( 'index'  => 'noindex',
+                                     'follow' => 'follow' );
+               }
+
+               # Otherwise, construct the policy based on the various config variables.
+               $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
+
+               if( isset( $wgNamespaceRobotPolicies[$ns] ) ){
                        # Honour customised robot policies for this namespace
-                       return $wgNamespaceRobotPolicies[$ns];
-               } else {
-                       return $wgDefaultRobotPolicy;
+                       $policy = array_merge( $policy,
+                                              self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] ) );
+               }
+               if( $this->mTitle->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ){
+                       # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
+                       # a final sanity check that we have really got the parser output.
+                       $policy = array_merge( $policy,
+                                              array( 'index' => $this->mParserOutput->getIndexPolicy() ) );
+               }
+
+               if( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ){
+                       # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
+                       $policy = array_merge( $policy,
+                                              self::formatRobotPolicy( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) );
                }
+
+               return $policy;
+
+       }
+
+       /**
+        * Converts a String robot policy into an associative array, to allow
+        * merging of several policies using array_merge().
+        * @param $policy Mixed, returns empty array on null/false/'', transparent
+        *            to already-converted arrays, converts String.
+        * @return associative Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
+        */
+       public static function formatRobotPolicy( $policy ){
+               if( is_array( $policy ) ){
+                       return $policy;
+               } elseif( !$policy ){
+                       return array();
+               }
+
+               $policy = explode( ',', $policy );
+               $policy = array_map( 'trim', $policy );
+
+               $arr = array();
+               foreach( $policy as $var ){
+                       if( in_array( $var, array('index','noindex') ) ){
+                               $arr['index'] = $var;
+                       } elseif( in_array( $var, array('follow','nofollow') ) ){
+                               $arr['follow'] = $var;
+                       }
+               }
+               return $arr;
        }
 
        /**
@@ -1118,9 +1199,25 @@ class Article {
         * namespace, show the default message text. To be called from Article::view().
         */
        public function showMissingArticle() {
-               global $wgOut, $wgRequest;
+               global $wgOut, $wgRequest, $wgUser;
+
+               # Show info in user (talk) namespace. Does the user exist?
+               if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
+                       $id = User::idFromName( $this->mTitle->getBaseText() );
+                       $ip = User::isIP( $this->mTitle->getBaseText() );
+                       if ( $id == 0 && !$ip ) { # User does not exist
+                               $wgOut->wrapWikiMsg( '<div class="mw-userpage-userdoesnotexist error">$1</div>',
+                                       array( 'userpage-userdoesnotexist-view', $this->mTitle->getBaseText() ) );
+                       }
+               }
+               wfRunHooks( 'ShowMissingArticle', array( $this ) );
                # Show delete and move logs
-               $this->showLogs();
+               LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(), '',
+                       array(  'lim' => 10,
+                               'conds' => array( "log_action != 'revision'" ),
+                               'showIfEmpty' => false,
+                               'msgKey' => array( 'moveddeleted-notice' ) ) 
+               );
 
                # Show error message
                $oldid = $this->getOldID();
@@ -1132,7 +1229,14 @@ class Article {
                        // Use the default message text
                        $text = $this->getContent();
                } else {
-                       $text = wfMsgNoTrans( 'noarticletext' );
+                       $createErrors = $this->mTitle->getUserPermissionsErrors( 'create', $wgUser );
+                       $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+                       $errors = array_merge( $createErrors, $editErrors );
+                       
+                       if ( !count($errors) )
+                               $text = wfMsgNoTrans( 'noarticletext' );
+                       else
+                               $text = wfMsgNoTrans( 'noarticletext-nopermission' );
                }
                $text = "<div class='noarticletext'>\n$text\n</div>";
                if( !$this->hasViewableContent() ) {
@@ -1150,12 +1254,10 @@ class Article {
         */
        public function showDeletedRevisionHeader() {
                global $wgOut, $wgRequest;
-
                if( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
                        // Not deleted
                        return true;
                }
-
                // If the user is not allowed to see it...
                if( !$this->mRevision->userCan(Revision::DELETED_TEXT) ) {
                        $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
@@ -1166,47 +1268,20 @@ class Article {
                        # Give explanation and add a link to view the revision...
                        $oldid = intval( $this->getOldID() );
                        $link = $this->mTitle->getFullUrl( "oldid={$oldid}&unhide=1" );
+                       $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                               'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
                        $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
-                               array('rev-deleted-text-unhide',$link) );
+                               array($msg,$link) );
                        return false;
                // We are allowed to see...
                } else {
-                       $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
-                               'rev-deleted-text-view' );
+                       $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                               'rev-suppressed-text-view' : 'rev-deleted-text-view';
+                       $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", $msg );
                        return true;
                }
        }
 
-       /**
-        * Show an excerpt from the deletion and move logs. To be called from the 
-        * header section on page views of missing pages.
-        */
-       public function showLogs() {
-               global $wgUser, $wgOut;
-               $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut );
-               $pager = new LogPager( $loglist, array('move', 'delete'), false,
-                       $this->mTitle->getPrefixedText(), '', array( "log_action != 'revision'" ) );
-               if( $pager->getNumRows() > 0 ) {
-                       $pager->mLimit = 10;
-                       $wgOut->addHTML( '<div class="mw-warning-with-logexcerpt">' );
-                       $wgOut->addWikiMsg( 'moveddeleted-notice' );
-                       $wgOut->addHTML(
-                               $loglist->beginLogEventsList() .
-                               $pager->getBody() .
-                               $loglist->endLogEventsList()
-                       );
-                       if( $pager->getNumRows() > 10 ) {
-                               $wgOut->addHTML( $wgUser->getSkin()->link(
-                                       SpecialPage::getTitleFor( 'Log' ),
-                                       wfMsgHtml( 'log-fulllog' ),
-                                       array(),
-                                       array( 'page' => $this->mTitle->getPrefixedText() )
-                               ) );
-                       }
-                       $wgOut->addHTML( '</div>' );
-               }
-       }
-
        /*
        * Should the parser cache be used?
        */
@@ -1253,6 +1328,7 @@ class Article {
                        wfDebug( __METHOD__.": sending dirty output\n" );
                        wfDebugLog( 'dirty', "dirty output " . $parserCache->getKey( $this, $options ) . "\n" );
                        $wgOut->setSquidMaxage( 0 );
+                       $this->mParserOutput = $output;
                        $wgOut->addParserOutput( $output );
                        $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
                        return true;
@@ -1291,7 +1367,7 @@ class Article {
                if( !is_array( $target ) ) {
                        $target = array( $target );
                }
-               $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+               $imageDir = $wgContLang->getDir();
                $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
                $imageUrl2 = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
                $alt2 = $wgContLang->isRTL() ? '&larr;' : '&rarr;'; // should -> and <- be used instead of entities?
@@ -1810,6 +1886,7 @@ class Article {
 
                $dbw = wfGetDB( DB_MASTER );
                $now = wfTimestampNow();
+               $this->mTimestamp=$now;
 
                if( $flags & EDIT_UPDATE ) {
                        # Update article, but only if changed.
@@ -2474,7 +2551,11 @@ class Article {
                if( $latest === false ) {
                        $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
                        $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
-                       LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
+                       LogEventsList::showLogExtract(
+                               $wgOut,
+                               'delete',
+                               $this->mTitle->getPrefixedText()
+                       );
                        return;
                }
 
@@ -2603,6 +2684,8 @@ class Article {
                $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) );
                $wgOut->setRobotPolicy( 'noindex,nofollow' );
                $wgOut->addWikiMsg( 'confirmdeletetext' );
+               
+               wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) );
 
                if( $wgUser->isAllowed( 'suppressrevision' ) ) {
                        $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\">
@@ -2637,8 +2720,13 @@ class Article {
                                        Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) .
                                "</td>
                                <td class='mw-input'>" .
-                                       Xml::input( 'wpReason', 60, $reason, array( 'type' => 'text', 'maxlength' => '255',
-                                               'tabindex' => '2', 'id' => 'wpReason' ) ) .
+                               Html::input( 'wpReason', $reason, 'text', array(
+                                       'size' => '60',
+                                       'maxlength' => '255',
+                                       'tabindex' => '2',
+                                       'id' => 'wpReason',
+                                       'autofocus'
+                               ) ) .
                                "</td>
                        </tr>
                        <tr>
@@ -2675,7 +2763,11 @@ class Article {
 
                $wgOut->addHTML( $form );
                $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
-               LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
+               LogEventsList::showLogExtract(
+                       $wgOut,
+                       'delete',
+                       $this->mTitle->getPrefixedText()
+               );
        }
 
        /**
@@ -2698,14 +2790,18 @@ class Article {
                                $wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
                                $wgOut->returnToMain( false );
                                wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
+                       }
+               } else {
+                       if( $error == '' ) {
+                               $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
+                               $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+                               LogEventsList::showLogExtract(
+                                       $wgOut,
+                                       'delete',
+                                       $this->mTitle->getPrefixedText()
+                               );
                        } else {
-                               if( $error == '' ) {
-                                       $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
-                                       $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
-                                       LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
-                               } else {
-                                       $wgOut->showFatalError( $error );
-                               }
+                               $wgOut->showFatalError( $error );
                        }
                }
        }
@@ -2913,6 +3009,8 @@ class Article {
                }
 
                $from = str_replace( '_', ' ', $fromP );
+               # User name given should match up with the top revision.
+               # If the user was deleted then $from should be empty.
                if( $from != $current->getUserText() ) {
                        $resultDetails = array( 'current' => $current );
                        return array(array('alreadyrolled',
@@ -2922,9 +3020,10 @@ class Article {
                        ));
                }
 
-               # Get the last edit not by this guy
-               $user = intval( $current->getUser() );
-               $user_text = $dbw->addQuotes( $current->getUserText() );
+               # Get the last edit not by this guy...
+               # Note: these may not be public values
+               $user = intval( $current->getRawUser() );
+               $user_text = $dbw->addQuotes( $current->getRawUserText() );
                $s = $dbw->selectRow( 'revision',
                        array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
                        array(  'rev_page' => $current->getPage(),
@@ -2951,20 +3050,24 @@ class Article {
                        $set['rc_patrolled'] = 1;
                }
 
-               if( $set ) {
+               if( count($set) ) {
                        $dbw->update( 'recentchanges', $set,
-                                       array( /* WHERE */
-                                               'rc_cur_id' => $current->getPage(),
-                                               'rc_user_text' => $current->getUserText(),
-                                               "rc_timestamp > '{$s->rev_timestamp}'",
-                                       ), __METHOD__
-                               );
+                               array( /* WHERE */
+                                       'rc_cur_id' => $current->getPage(),
+                                       'rc_user_text' => $current->getUserText(),
+                                       "rc_timestamp > '{$s->rev_timestamp}'",
+                               ), __METHOD__
+                       );
                }
 
                # Generate the edit summary if necessary
                $target = Revision::newFromId( $s->rev_id );
-               if( empty( $summary ) ){
-                       $summary = wfMsgForContent( 'revertpage' );
+               if( empty( $summary ) ) {
+                       if( $from == '' ) { // no public user name
+                               $summary = wfMsgForContent( 'revertpage-nouser' );
+                       } else {
+                               $summary = wfMsgForContent( 'revertpage' );
+                       }
                }
 
                # Allow the custom summary to use the same args as the default message
@@ -2996,8 +3099,8 @@ class Article {
                $resultDetails = array(
                        'summary' => $summary,
                        'current' => $current,
-                       'target' => $target,
-                       'newid' => $revId
+                       'target'  => $target,
+                       'newid'   => $revId
                );
                return array();
        }
@@ -3079,6 +3182,9 @@ class Article {
         */
        public function viewUpdates() {
                global $wgDeferredUpdateList, $wgDisableCounters, $wgUser;
+               if ( wfReadOnly() ) {
+                       return;
+               }
                # Don't update page view counters on views from bot users (bug 14044)
                if( !$wgDisableCounters && !$wgUser->isAllowed('bot') && $this->getID() ) {
                        Article::incViewCount( $this->getID() );
@@ -3360,7 +3466,8 @@ class Article {
                                $userlinks,
                                $revision->getID(),
                                $tddate,
-                               $tdtime
+                               $tdtime,
+                               $revision->getUser()
                        ) .
                        "</div>\n" .
                        "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
@@ -3835,9 +3942,9 @@ class Article {
         */
        public function outputWikiText( $text, $cache = true, $parserOptions = false ) {
                global $wgOut;
-               
-               $parserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
-               $wgOut->addParserOutput( $parserOutput );
+
+               $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
+               $wgOut->addParserOutput( $this->mParserOutput );
        }
        
        /**
@@ -3853,7 +3960,7 @@ class Article {
                }
 
                $time = -wfTime();
-               $parserOutput = $wgParser->parse( $text, $this->mTitle,
+               $this->mParserOutput = $wgParser->parse( $text, $this->mTitle,
                        $parserOptions, true, true, $this->getRevIdFetched() );
                $time += wfTime();
 
@@ -3863,18 +3970,18 @@ class Article {
                                $this->mTitle->getPrefixedDBkey()));
                }
 
-               if( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+               if( $wgEnableParserCache && $cache && $this && $this->mParserOutput->getCacheTime() != -1 ) {
                        $parserCache = ParserCache::singleton();
-                       $parserCache->save( $parserOutput, $this, $parserOptions );
+                       $parserCache->save( $this->mParserOutput, $this, $parserOptions );
                }
                // Make sure file cache is not used on uncacheable content.
                // Output that has magic words in it can still use the parser cache
                // (if enabled), though it will generally expire sooner.
-               if( $parserOutput->getCacheTime() == -1 || $parserOutput->containsOldMagic() ) {
+               if( $this->mParserOutput->getCacheTime() == -1 || $this->mParserOutput->containsOldMagic() ) {
                        $wgUseFileCache = false;
                }
-               $this->doCascadeProtectionUpdates( $parserOutput );
-               return $parserOutput;
+               $this->doCascadeProtectionUpdates( $this->mParserOutput );
+               return $this->mParserOutput;
        }
 
        /**