*Redo newRevisionFromEditComplete hook. Pass an Article instead of a title
[lhc/web/wiklou.git] / includes / Article.php
index 3f3cdff..dedf4e7 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 /**
  * File for articles
+ * @file
  */
 
 /**
@@ -35,6 +36,7 @@ class Article {
        var $mUser;                             //!<
        var $mUserText;                 //!<
        var $mRedirectTarget;           //!<
+       var $mIsRedirect;
        /**@}}*/
 
        /**
@@ -71,7 +73,7 @@ class Article {
                        return $this->mRedirectTarget;
 
                # Query the redirect table
-               $dbr = wfGetDb(DB_SLAVE);
+               $dbr = wfGetDB(DB_SLAVE);
                $res = $dbr->select('redirect',
                                array('rd_namespace', 'rd_title'),
                                array('rd_from' => $this->getID()),
@@ -95,20 +97,23 @@ class Article {
                $retval = Title::newFromRedirect($this->getContent());
                if(!$retval)
                        return null;
-               $dbw = wfGetDb(DB_MASTER);
-               $dbw->insert('redirect', array(
+               $dbw = wfGetDB(DB_MASTER);
+               $dbw->replace('redirect', array('rd_from'), array(
                                'rd_from' => $this->getID(),
                                'rd_namespace' => $retval->getNamespace(),
                                'rd_title' => $retval->getDBKey()
-               ));
+               ), __METHOD__);
                return $retval;
        }
 
        /**
+        * Get the Title object this page redirects to
+        *
         * @return mixed false, Title of in-wiki target, or string with URL
         */
        function followRedirect() {
-               $rt = $this->getRedirectTarget();
+               $text = $this->getContent();
+               $rt = Title::newFromRedirect( $text );
 
                # process if title object is valid and not special:userlogout
                if( $rt ) {
@@ -391,7 +396,7 @@ class Article {
                # fails we'll have something telling us what we intended.
                $t = $this->mTitle->getPrefixedText();
                if( $oldid ) {
-                       $t .= ',oldid='.$oldid;
+                       $t .= ' ' . wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid );
                }
                $this->mContent = wfMsg( 'missingarticle', $t ) ;
 
@@ -538,6 +543,10 @@ class Article {
         */
        function isRedirect( $text = false ) {
                if ( $text === false ) {
+                       if ( $this->mDataLoaded ) 
+                               return $this->mIsRedirect;
+                       
+                       // Apparently loadPageData was never called
                        $this->loadContent();
                        $titleObj = Title::newFromRedirect( $this->fetchContent() );
                } else {
@@ -657,7 +666,7 @@ class Article {
         * This is the default action of the script: just view the page of
         * the given title.
        */
-       function view() {
+       function view() {
                global $wgUser, $wgOut, $wgRequest, $wgContLang;
                global $wgEnableParserCache, $wgStylePath, $wgParser;
                global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
@@ -794,10 +803,10 @@ class Article {
                                # Failed to load, replace text with error message
                                $t = $this->mTitle->getPrefixedText();
                                if( $oldid ) {
-                                       $t .= ',oldid='.$oldid;
+                                       $t .= ' ' . wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid );
                                        $text = wfMsg( 'missingarticle', $t );
                                } else {
-                                       $text = wfMsg( 'noarticletext', $t );
+                                       $text = wfMsg( 'noarticletext' );
                                }
                        }
 
@@ -805,6 +814,7 @@ class Article {
                        if ( !$this->mTitle->userCanRead() ) {
                                $wgOut->loginToUse();
                                $wgOut->output();
+                               wfProfileOut( __METHOD__ );
                                exit;
                        }
 
@@ -820,6 +830,7 @@ class Article {
                                                if( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
                                                        $wgOut->addWikiMsg( 'rev-deleted-text-permission' );
                                                        $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+                                                       wfProfileOut( __METHOD__ );
                                                        return;
                                                } else {
                                                        $wgOut->addWikiMsg( 'rev-deleted-text-view' );
@@ -832,7 +843,7 @@ class Article {
                }
                if( !$outputDone ) {
                        $wgOut->setRevisionId( $this->getRevIdFetched() );
-                       
+
                         // Pages containing custom CSS or JavaScript get special treatment
                        if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
                                $wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
@@ -846,22 +857,12 @@ class Article {
                                        $wgOut->addHtml( htmlspecialchars( $this->mContent ) );
                                        $wgOut->addHtml( "\n</pre>\n" );
                                }
-                       
-                       }
-                       
-                       elseif ( $rt = $this->getRedirectTarget() ) {
-                               # Display redirect
-                               $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
-                               $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
-                               # Don't overwrite the subtitle if this was an old revision
-                               if( !$wasRedirected && $this->isCurrent() ) {
-                                       $wgOut->setSubtitle( wfMsgHtml( 'redirectpagesub' ) );
-                               }
-                               $link = $sk->makeLinkObj( $rt, $rt->getFullText() );
 
-                               $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
-                                 '<span class="redirectText">'.$link.'</span>' );
+                       }
 
+                       elseif ( $rt = Title::newFromRedirect( $text ) ) {
+                               # Don't overwrite the subtitle if this was an old revision
+                               $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() );
                                $parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
                                $wgOut->addParserOutputNoText( $parseout );
                        } else if ( $pcache ) {
@@ -899,7 +900,7 @@ class Article {
 
                # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
                if( $ns == NS_USER_TALK &&
-                       User::isIP( $this->mTitle->getText() ) ) {
+                       IP::isValid( $this->mTitle->getText() ) ) {
                        $wgOut->addWikiMsg('anontalkpagetext');
                }
 
@@ -923,6 +924,27 @@ class Article {
                $this->viewUpdates();
                wfProfileOut( __METHOD__ );
        }
+       
+       protected function viewRedirect( $target, $overwriteSubtitle = true, $forceKnown = false ) {
+               global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
+               
+               # Display redirect
+               $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+               $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
+               
+               if( $overwriteSubtitle ) {
+                       $wgOut->setSubtitle( wfMsgHtml( 'redirectpagesub' ) );
+               }
+               $sk = $wgUser->getSkin();
+               if ( $forceKnown )
+                       $link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+               else
+                       $link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+
+               $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
+                       '<span class="redirectText">'.$link.'</span>' );
+               
+       }
 
        function addTrackbacks() {
                global $wgOut, $wgUser;
@@ -1160,6 +1182,8 @@ class Article {
                                $dbw->delete( 'redirect', $where, __METHOD__);
                        }
 
+                       if( $this->getTitle()->getNamespace() == NS_IMAGE )
+                               RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
                        wfProfileOut( __METHOD__ );
                        return ( $dbw->affectedRows() != 0 );
                }
@@ -1339,10 +1363,11 @@ class Article {
         * EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
         * to be thrown from the Database. These two conditions are also possible with auto-detection due
         * to MediaWiki's performance-optimised locking strategy.
+        * @param $baseRevId, the revision ID this edit was based off, if any
         *
         * @return bool success
         */
-       function doEdit( $text, $summary, $flags = 0 ) {
+       function doEdit( $text, $summary, $flags = 0, $baseRevId = false ) {
                global $wgUser, $wgDBtransactions;
 
                wfProfileIn( __METHOD__ );
@@ -1394,7 +1419,7 @@ class Article {
 
                        $lastRevision = 0;
                        $revisionId = 0;
-                       
+
                        $changed = ( strcmp( $text, $oldtext ) != 0 );
 
                        if ( $changed ) {
@@ -1425,6 +1450,8 @@ class Article {
 
                                # Update page
                                $ok = $this->updateRevisionOn( $dbw, $revision, $lastRevision );
+                               
+                               wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, $baseRevId) );
 
                                if( !$ok ) {
                                        /* Belated edit conflict! Run away!! */
@@ -1463,7 +1490,7 @@ class Article {
                                # Invalidate cache of this article and all pages using this article
                                # as a template. Partly deferred.
                                Article::onArticleEdit( $this->mTitle );
-                               
+
                                # Update links tables, site stats, etc.
                                $this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
                        }
@@ -1495,6 +1522,8 @@ class Article {
 
                        # Update the page record with revision data
                        $this->updateRevisionOn( $dbw, $revision, 0 );
+                       
+                       wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
 
                        if( !( $flags & EDIT_SUPPRESS_RC ) ) {
                                $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
@@ -1569,7 +1598,7 @@ class Article {
 
                if ( !($wgUseNPPatrol || $wgUseRCPatrol)) {
                        $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
-                       return;         
+                       return;
                }
 
                # If we haven't been given an rc_id value, we can't do anything
@@ -1588,7 +1617,7 @@ class Article {
                        $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
                        return;
                }
-               
+
                # Check permissions
                $permission_errors = $this->mTitle->getUserPermissionsErrors( 'patrol', $wgUser );
 
@@ -1778,8 +1807,8 @@ class Article {
                $updated = Article::flattenRestrictions( $limit );
 
                $changed = ( $current != $updated );
-               $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
-               $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
+               $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
+               $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry);
                $protect = ( $updated != '' );
 
                # If nothing's changed, do nothing
@@ -1808,10 +1837,10 @@ class Article {
                                foreach( $limit as $action => $restrictions ) {
                                        # Check if the group level required to edit also can protect pages
                                        # Otherwise, people who cannot normally protect can "protect" pages via transclusion
-                                       $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && 
-                                               $wgGroupPermissions[$restrictions]['protect'] );        
+                                       $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) &&
+                                               $wgGroupPermissions[$restrictions]['protect'] );
                                }
-                               
+
                                $cascade_description = '';
                                if ($cascade) {
                                        $cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
@@ -1825,7 +1854,7 @@ class Article {
                                        $comment .= "$expiry_description";
                                if ( $cascade )
                                        $comment .= "$cascade_description";
-                               
+
                                # Update restrictions table
                                foreach( $limit as $action => $restrictions ) {
                                        if ($restrictions != '' ) {
@@ -1853,13 +1882,12 @@ class Article {
                                                'page_id' => $id
                                        ), 'Article::protect'
                                );
+                               
+                               wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, false) );
                                wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
 
                                # Update the protection log
                                $log = new LogPage( 'protect' );
-                               
-                               
-
                                if( $protect ) {
                                        $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
                                } else {
@@ -1892,7 +1920,7 @@ class Article {
                }
                return implode( ':', $bits );
        }
-       
+
        /**
         * Auto-generates a deletion reason
         * @param bool &$hasHistory Whether the page has a history
@@ -1954,7 +1982,7 @@ class Article {
                        else
                                $reason = wfMsgForContent('excontent', '$1');
                }
-               
+
                // Replace newlines with spaces to prevent uglyness
                $contents = preg_replace("/[\n\r]/", ' ', $contents);
                // Calculate the maximum amount of chars to get
@@ -1977,12 +2005,12 @@ class Article {
 
                $confirm = $wgRequest->wasPosted() &&
                                $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
-               
+
                $this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
                $this->DeleteReason = $wgRequest->getText( 'wpReason' );
-               
+
                $reason = $this->DeleteReasonList;
-               
+
                if ( $reason != 'other' && $this->DeleteReason != '') {
                        // Entry from drop down menu + additional comment
                        $reason .= ': ' . $this->DeleteReason;
@@ -1990,7 +2018,7 @@ class Article {
                        $reason = $this->DeleteReason;
                }
                # Flag to hide all contents of the archived revisions
-               $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('deleterevision');
+               $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('hiderevision');
 
                # This code desperately needs to be totally rewritten
 
@@ -1999,7 +2027,7 @@ class Article {
                        $wgOut->readOnlyPage();
                        return;
                }
-               
+
                # Check permissions
                $permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
 
@@ -2052,10 +2080,10 @@ class Article {
                                        array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
                        }
                }
-               
+
                return $this->confirmDelete( '', $reason );
        }
-       
+
        /**
         * @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
         */
@@ -2067,7 +2095,7 @@ class Article {
                }
                return false;
        }
-       
+
        /**
         * @return int approximate revision count
         */
@@ -2141,7 +2169,7 @@ class Article {
                $wgOut->setRobotpolicy( 'noindex,nofollow' );
                $wgOut->addWikiMsg( 'confirmdeletetext' );
 
-               if( $wgUser->isAllowed( 'deleterevision' ) ) {
+               if( $wgUser->isAllowed( 'hiderevision' ) ) {
                        $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\"><td></td><td>";
                        $suppress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
                        $suppress .= "</td></tr>";
@@ -2159,7 +2187,7 @@ class Article {
                                "</td>
                                <td>" .
                                        Xml::listDropDown( 'wpDeleteReasonList',
-                                               wfMsgForContent( 'deletereason-dropdown' ), 
+                                               wfMsgForContent( 'deletereason-dropdown' ),
                                                wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
                                "</td>
                        </tr>
@@ -2215,6 +2243,8 @@ class Article {
        function doDelete( $reason, $suppress = false ) {
                global $wgOut, $wgUser;
                wfDebug( __METHOD__."\n" );
+               
+               $id = $this->getId();
 
                if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
                        if ( $this->doDeleteArticle( $reason, $suppress ) ) {
@@ -2227,9 +2257,9 @@ class Article {
 
                                $wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
                                $wgOut->returnToMain( false );
-                               wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason));
+                               wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
                        } else {
-                               $wgOut->showFatalError( wfMsg( 'cannotdelete' ).'<br/>'.wfMsg('cannotdelete-merge') );
+                               $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
                        }
                }
        }
@@ -2268,7 +2298,8 @@ class Article {
                } else {
                        $bitfield = 'rev_deleted';
                }
-               
+
+               $dbw->begin();
                // For now, shunt the revision data into the archive table.
                // Text is *not* removed from the text table; bulk storage
                // is left intact to avoid breaking block-compression or
@@ -2315,6 +2346,11 @@ class Article {
 
                # Now that it's safely backed up, delete it
                $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
+               $ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
+               if( !$ok ) {
+                       $dbw->rollback();
+                       return false;
+               }
 
                # If using cascading deletes, we can skip some explicit deletes
                if ( !$dbw->cascadingDeletes() ) {
@@ -2337,8 +2373,11 @@ class Article {
                if ( !$dbw->cleanupTriggers() ) {
 
                        # Clean up recentchanges entries...
-                       $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
+                       $dbw->delete( 'recentchanges',
+                               array( 'rc_namespace' => $ns, 'rc_title' => $t, 'rc_type != '.RC_LOG ),
+                               __METHOD__ );
                }
+               $dbw->commit();
 
                # Clear caches
                Article::onArticleDelete( $this->mTitle );
@@ -2350,8 +2389,10 @@ class Article {
                # Log the deletion, if the page was suppressed, log it at Oversight instead
                $logtype = $suppress ? 'suppress' : 'delete';
                $log = new LogPage( $logtype );
-               $log->addEntry( 'delete', $this->mTitle, $reason );
-               
+
+               # Make sure logging got through
+               $log->addEntry( 'delete', $this->mTitle, $reason, array() );
+
                return true;
        }
 
@@ -2362,15 +2403,15 @@ class Article {
         * performs permissions checks on $wgUser, then calls commitRollback()
         * to do the dirty work
         *
-        * @param string $fromP - Name of the user whose edits to rollback. 
+        * @param string $fromP - Name of the user whose edits to rollback.
         * @param string $summary - Custom summary. Set to default summary if empty.
         * @param string $token - Rollback token.
         * @param bool   $bot - If true, mark all reverted edits as bot.
-        * 
+        *
         * @param array $resultDetails contains result-specific array of additional values
         *    'alreadyrolled' : 'current' (rev)
         *    success        : 'summary' (str), 'current' (rev), 'target' (rev)
-        * 
+        *
         * @return array of errors, each error formatted as
         *   array(messagekey, param1, param2, ...).
         * On success, the array is empty.  This array can also be passed to
@@ -2392,10 +2433,10 @@ class Article {
                # If there were errors, bail out now
                if(!empty($errors))
                        return $errors;
-               
+
                return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
        }
-       
+
        /**
         * Backend implementation of doRollback(), please refer there for parameter
         * and return value documentation
@@ -2404,9 +2445,9 @@ class Article {
         * rollback to the DB Therefore, you should only call this function direct-
         * ly if you want to use custom permissions checks. If you don't, use
         * doRollback() instead.
-        */     
+        */
        public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
-               global $wgUseRCPatrol, $wgUser;
+               global $wgUseRCPatrol, $wgUser, $wgLang;
                $dbw = wfGetDB( DB_MASTER );
 
                if( wfReadOnly() ) {
@@ -2448,7 +2489,7 @@ class Article {
                        # Only admins can see this text
                        return array(array('notvisiblerev'));
                }
-       
+
                $set = array();
                if ( $bot && $wgUser->isAllowed('markbotedits') ) {
                        # Mark all reverted edits as bot
@@ -2471,15 +2512,17 @@ class Article {
 
                # Generate the edit summary if necessary
                $target = Revision::newFromId( $s->rev_id );
-               if( empty( $summary ) )
-               {
-                       global $wgLang;
-                       $summary = wfMsgForContent( 'revertpage',
-                                        $target->getUserText(), $from,
-                                        $s->rev_id, $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
-                                        $current->getId(), $wgLang->timeanddate($current->getTimestamp())
-                       );
+               if( empty( $summary ) ){
+                       $summary = wfMsgForContent( 'revertpage' );
                }
+               
+               # Allow the custom summary to use the same args as the default message
+               $args = array(
+                       $target->getUserText(), $from, $s->rev_id,
+                       $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
+                       $current->getId(), $wgLang->timeanddate($current->getTimestamp())
+               );
+               $summary = wfMsgReplaceArgs( $summary, $args ); 
 
                # Save
                $flags = EDIT_UPDATE;
@@ -2489,7 +2532,7 @@ class Article {
 
                if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
                        $flags |= EDIT_FORCE_BOT;
-               $this->doEdit( $target->getText(), $summary, $flags );
+               $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
 
                wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target ) );
 
@@ -2575,11 +2618,12 @@ class Article {
         * @private
         */
        function viewUpdates() {
-               global $wgDeferredUpdateList;
+               global $wgDeferredUpdateList, $wgUser;
 
                if ( 0 != $this->getID() ) {
+                       # Don't update page view counters on views from bot users (bug 14044)
                        global $wgDisableCounters;
-                       if( !$wgDisableCounters ) {
+                       if( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) ) {
                                Article::incViewCount( $this->getID() );
                                $u = new SiteStatsUpdate( 1, 0, 0 );
                                array_push( $wgDeferredUpdateList, $u );
@@ -2587,7 +2631,6 @@ class Article {
                }
 
                # Update newtalk / watchlist notification status
-               global $wgUser;
                $wgUser->clearNotification( $this->mTitle );
        }
 
@@ -2763,14 +2806,14 @@ class Article {
                        : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
 
                $cdel='';
-               if( $wgUser->isAllowed( 'deleterevision' ) ) {          
+               if( $wgUser->isAllowed( 'deleterevision' ) ) {
                        $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
                        if( $revision->isCurrent() ) {
                        // We don't handle top deleted edits too well
-                               $cdel = wfMsgHtml('rev-delundel');      
+                               $cdel = wfMsgHtml('rev-delundel');
                        } else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
                        // If revision was hidden from sysops
-                               $cdel = wfMsgHtml('rev-delundel');      
+                               $cdel = wfMsgHtml('rev-delundel');
                        } else {
                                $cdel = $sk->makeKnownLinkObj( $revdel,
                                        wfMsgHtml('rev-delundel'),
@@ -2790,7 +2833,7 @@ class Article {
                $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
                        ? 'revision-info-current'
                        : 'revision-info';
-                       
+
                $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
 
                     "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
@@ -2851,9 +2894,9 @@ class Article {
                $printable = $wgRequest->getVal( 'printable' );
                $page      = $wgRequest->getVal( 'page' );
 
-               //check for non-standard user language; this covers uselang, 
+               //check for non-standard user language; this covers uselang,
                //and extensions for auto-detecting user language.
-               $ulang     = $wgLang->getCode(); 
+               $ulang     = $wgLang->getCode();
                $clang     = $wgContLang->getCode();
 
                $cacheable = $wgUseFileCache
@@ -2934,6 +2977,8 @@ class Article {
                $revision->insertOn( $dbw );
                $this->updateRevisionOn( $dbw, $revision );
                $dbw->commit();
+               
+               wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
 
                wfProfileOut( __METHOD__ );
        }
@@ -3039,7 +3084,7 @@ class Article {
                }
                $other->invalidateCache();
                $other->purgeSquid();
-               
+
                $title->touchLinks();
                $title->purgeSquid();
 
@@ -3049,13 +3094,20 @@ class Article {
                        @unlink( $cm->fileCacheName() );
                }
 
-               if( $title->getNamespace() == NS_MEDIAWIKI) {
+               # Messages
+               if( $title->getNamespace() == NS_MEDIAWIKI ) {
                        $wgMessageCache->replace( $title->getDBkey(), false );
                }
+               # Images
                if( $title->getNamespace() == NS_IMAGE ) {
                        $update = new HTMLCacheUpdate( $title, 'imagelinks' );
                        $update->doUpdate();
                }
+               # User talk pages
+               if( $title->getNamespace() == NS_USER_TALK ) {
+                       $user = User::newFromName( $title->getText(), false );
+                       $user->setNewtalk( false );
+               }
        }
 
        /**
@@ -3069,7 +3121,7 @@ class Article {
 
                // Invalidate the caches of all pages which redirect here
                $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
-               
+
                # Purge squid for this page only
                $title->purgeSquid();
 
@@ -3233,7 +3285,7 @@ class Article {
                $dbr = wfGetDB( DB_SLAVE );
                $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
                        array( 'cl_to' ),
-                       array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat', 
+                       array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
                                'page_namespace' => NS_CATEGORY, 'page_title=cl_to'),
                        'Article:getHiddenCategories' );
                if ( false !== $res ) {
@@ -3290,6 +3342,8 @@ class Article {
        * @return string An appropriate autosummary, or an empty string.
        */
        public static function getAutosummary( $oldtext, $newtext, $flags ) {
+               global $wgUseAutomaticEditSummaries;
+               if ( !$wgUseAutomaticEditSummaries ) return '';
 
                # This code is UGLY UGLY UGLY.
                # Somebody PLEASE come up with a more elegant way to do it.