Did some refactoring in Article.php:
authorTim Starling <tstarling@users.mediawiki.org>
Tue, 20 Jun 2006 09:50:57 +0000 (09:50 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Tue, 20 Jun 2006 09:50:57 +0000 (09:50 +0000)
* Introduced doEdit(), a simplified editing API. Rewrote updateArticle() and insertNewArticle() to call doEdit() and marked them deprecated. Callers should be updated to use doEdit() at a later date.
* Replaced $fname with __METHOD__
* Removed some unused member variables
* Fixed cache purging on null edit, hopefully it should work the same as action=purge now.
* Fixed doRedirect() callers, minor bug introduced with HTMLCacheUpdate.

includes/Article.php
includes/Defines.php

index 13dc5a0..ea1d7f9 100644 (file)
@@ -26,10 +26,8 @@ class Article {
        var $mContent;                  //!<
        var $mContentLoaded;    //!<
        var $mCounter;                  //!<
-       var $mFileCache;                //!<
        var $mForUpdate;                //!<
        var $mGoodAdjustment;   //!<
-       var $mId;                               //!<
        var $mLatest;                   //!<
        var $mMinorEdit;                //!<
        var $mOldId;                    //!<
@@ -37,7 +35,6 @@ class Article {
        var $mRedirectUrl;              //!<
        var $mRevIdFetched;             //!<
        var $mRevision;                 //!<
-       var $mTable;                    //!<
        var $mTimestamp;                //!<
        var $mTitle;                    //!<
        var $mTotalAdjustment;  //!<
@@ -125,7 +122,7 @@ class Article {
                $this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded
                $this->mRedirectedFrom = null; # Title object if set
                $this->mUserText =
-               $this->mTimestamp = $this->mComment = $this->mFileCache = '';
+               $this->mTimestamp = $this->mComment = '';
                $this->mGoodAdjustment = $this->mTotalAdjustment = 0;
                $this->mTouched = '19700101000000';
                $this->mForUpdate = false;
@@ -154,12 +151,11 @@ class Article {
                $section = $wgRequest->getText( 'section' );
                $preload = $wgRequest->getText( 'preload' );
 
-               $fname =  'Article::getContent';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                if ( 0 == $this->getID() ) {
                        if ( 'edit' == $action ) {
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
 
                                # If requested, preload some text.
                                $text=$this->getPreloadedText($preload);
@@ -169,7 +165,7 @@ class Article {
                                # This is now shown above the edit box instead.
                                return $text;
                        }
-                       wfProfileOut( $fname );
+                       wfProfileOut( __METHOD__ );
                        $wgOut->setRobotpolicy( 'noindex,nofollow' );
 
                        if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
@@ -184,7 +180,7 @@ class Article {
                        if($action=='edit') {
                                if($section!='') {
                                        if($section=='new') {
-                                               wfProfileOut( $fname );
+                                               wfProfileOut( __METHOD__ );
                                                $text=$this->getPreloadedText($preload);
                                                return $text;
                                        }
@@ -192,11 +188,11 @@ class Article {
                                        # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
                                        # comments to be stripped as well)
                                        $rv=$this->getSection($this->mContent,$section);
-                                       wfProfileOut( $fname );
+                                       wfProfileOut( __METHOD__ );
                                        return $rv;
                                }
                        }
-                       wfProfileOut( $fname );
+                       wfProfileOut( __METHOD__ );
                        return $this->mContent;
                }
        }
@@ -400,7 +396,6 @@ class Article {
                }
 
                $dbr =& $this->getDB();
-               $fname = 'Article::fetchContent';
 
                # Pre-fill content with error message so that if something
                # fails we'll have something telling us what we intended.
@@ -413,12 +408,12 @@ class Article {
                if( $oldid ) {
                        $revision = Revision::newFromId( $oldid );
                        if( is_null( $revision ) ) {
-                               wfDebug( "$fname failed to retrieve specified revision, id $oldid\n" );
+                               wfDebug( __METHOD__." failed to retrieve specified revision, id $oldid\n" );
                                return false;
                        }
                        $data = $this->pageDataFromId( $dbr, $revision->getPage() );
                        if( !$data ) {
-                               wfDebug( "$fname failed to get page data linked to revision id $oldid\n" );
+                               wfDebug( __METHOD__." failed to get page data linked to revision id $oldid\n" );
                                return false;
                        }
                        $this->mTitle = Title::makeTitle( $data->page_namespace, $data->page_title );
@@ -427,14 +422,14 @@ class Article {
                        if( !$this->mDataLoaded ) {
                                $data = $this->pageDataFromTitle( $dbr, $this->mTitle );
                                if( !$data ) {
-                                       wfDebug( "$fname failed to find page data for title " . $this->mTitle->getPrefixedText() . "\n" );
+                                       wfDebug( __METHOD__." failed to find page data for title " . $this->mTitle->getPrefixedText() . "\n" );
                                        return false;
                                }
                                $this->loadPageData( $data );
                        }
                        $revision = Revision::newFromId( $this->mLatest );
                        if( is_null( $revision ) ) {
-                               wfDebug( "$fname failed to retrieve current page, rev_id {$data->page_latest}\n" );
+                               wfDebug( __METHOD__." failed to retrieve current page, rev_id {$data->page_latest}\n" );
                                return false;
                        }
                }
@@ -637,8 +632,6 @@ class Article {
         * @param $offset Integer: default 0.
         */
        function getContributors($limit = 0, $offset = 0) {
-               $fname = 'Article::getContributors';
-
                # XXX: this is expensive; cache this info somewhere.
 
                $title = $this->mTitle;
@@ -661,7 +654,7 @@ class Article {
                if ($limit > 0) { $sql .= ' LIMIT '.$limit; }
                $sql .= ' '. $this->getSelectOptions();
 
-               $res = $dbr->query($sql, $fname);
+               $res = $dbr->query($sql, __METHOD__);
 
                while ( $line = $dbr->fetchObject( $res ) ) {
                        $contribs[] = array($line->rev_user, $line->rev_user_text, $line->user_real_name);
@@ -681,8 +674,7 @@ class Article {
                global $wgUseTrackbacks, $wgNamespaceRobotPolicies;
                $sk = $wgUser->getSkin();
 
-               $fname = 'Article::view';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                $parserCache =& ParserCache::singleton();
                $ns = $this->mTitle->getNamespace(); # shortcut
@@ -693,7 +685,7 @@ class Article {
                # getOldID may want us to redirect somewhere else
                if ( $this->mRedirectUrl ) {
                        $wgOut->redirect( $this->mRedirectUrl );
-                       wfProfileOut( $fname );
+                       wfProfileOut( __METHOD__ );
                        return;
                }
 
@@ -725,7 +717,7 @@ class Article {
                                # Run view updates for current revision only
                                $this->viewUpdates();
                        }
-                       wfProfileOut( $fname );
+                       wfProfileOut( __METHOD__ );
                        return;
                }
                
@@ -733,13 +725,13 @@ class Article {
                        $wgOut->setETag($parserCache->getETag($this, $wgUser));
 
                        if( $wgOut->checkLastModified( $this->mTouched ) ){
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return;
                        } else if ( $this->tryFileCache() ) {
                                # tell wgOut that output is taken care of
                                $wgOut->disable();
                                $this->viewUpdates();
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return;
                        }
                }
@@ -904,7 +896,7 @@ class Article {
                        $this->addTrackbacks();
 
                $this->viewUpdates();
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
        }
 
        function addTrackbacks() {
@@ -1027,8 +1019,7 @@ class Article {
         * @private
         */
        function insertOn( &$dbw, $restrictions = '' ) {
-               $fname = 'Article::insertOn';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                $page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
                $dbw->insert( 'page', array(
@@ -1043,12 +1034,12 @@ class Article {
                        'page_touched'      => $dbw->timestamp(),
                        'page_latest'       => 0, # Fill this in shortly...
                        'page_len'          => 0, # Fill this in shortly...
-               ), $fname );
+               ), __METHOD__ );
                $newid = $dbw->insertId();
 
                $this->mTitle->resetArticleId( $newid );
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
                return $newid;
        }
 
@@ -1066,8 +1057,7 @@ class Article {
         * @private
         */
        function updateRevisionOn( &$dbw, $revision, $lastRevision = null ) {
-               $fname = 'Article::updateToRevision';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                $conditions = array( 'page_id' => $this->getId() );
                if( !is_null( $lastRevision ) ) {
@@ -1085,9 +1075,9 @@ class Article {
                                'page_len'         => strlen( $text ),
                        ),
                        $conditions,
-                       $fname );
+                       __METHOD__ );
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
                return ( $dbw->affectedRows() != 0 );
        }
 
@@ -1099,8 +1089,7 @@ class Article {
         * @param Revision $revision
         */
        function updateIfNewerOn( &$dbw, $revision ) {
-               $fname = 'Article::updateIfNewerOn';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                $row = $dbw->selectRow(
                        array( 'revision', 'page' ),
@@ -1108,10 +1097,10 @@ class Article {
                        array(
                                'page_id' => $this->getId(),
                                'page_latest=rev_id' ),
-                       $fname );
+                       __METHOD__ );
                if( $row ) {
                        if( wfTimestamp(TS_MW, $row->rev_timestamp) >= $revision->getTimestamp() ) {
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return false;
                        }
                        $prev = $row->rev_id;
@@ -1121,115 +1110,15 @@ class Article {
                }
 
                $ret = $this->updateRevisionOn( $dbw, $revision, $prev );
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
                return $ret;
        }
 
-       /**
-        * Insert a new article into the database
-        * @private
-        */
-       function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false ) {
-               global $wgUser;
-
-               $fname = 'Article::insertNewArticle';
-               wfProfileIn( $fname );
-
-               if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text,
-                       &$summary, &$isminor, &$watchthis, NULL ) ) ) {
-                       wfDebug( "$fname: ArticleSave hook aborted save!\n" );
-                       wfProfileOut( $fname );
-                       return false;
-               }
-
-               $ns = $this->mTitle->getNamespace();
-               $ttl = $this->mTitle->getDBkey();
-
-               # If this is a comment, add the summary as headline
-               if($comment && $summary!="") {
-                       $text="== {$summary} ==\n\n".$text;
-               }
-               $text = $this->preSaveTransform( $text );
-
-
-               # Set statistics members
-               # We work out if it's countable after PST to avoid counter drift 
-               # when articles are created with {{subst:}}
-               $this->mGoodAdjustment = (int)$this->isCountable( $text );
-               $this->mTotalAdjustment = 1;
-
-               /* Silently ignore minoredit if not allowed */
-               $isminor = $isminor && $wgUser->isAllowed('minoredit');
-               $now = wfTimestampNow();
-
-               $dbw =& wfGetDB( DB_MASTER );
-
-               # Add the page record; stake our claim on this title!
-               $newid = $this->insertOn( $dbw );
-
-               # Save the revision text...
-               $revision = new Revision( array(
-                       'page'       => $newid,
-                       'comment'    => $summary,
-                       'minor_edit' => $isminor,
-                       'text'       => $text
-                       ) );
-               $revisionId = $revision->insertOn( $dbw );
-
-               $this->mTitle->resetArticleID( $newid );
-
-               # Update the page record with revision data
-               $this->updateRevisionOn( $dbw, $revision, 0 );
-
-               if(!$suppressRC) {
-                       $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, 'default',
-                         '', strlen( $text ), $revisionId );
-                       # Mark as patrolled if the user can and has the option set
-                       if( $wgUser->isAllowed( 'patrol' ) && $wgUser->getOption( 'autopatrol' ) ) {
-                               RecentChange::markPatrolled( $rcid );
-                       }
-               }
-
-               if ($watchthis) {
-                       if(!$this->mTitle->userIsWatching()) $this->doWatch();
-               } else {
-                       if ( $this->mTitle->userIsWatching() ) {
-                               $this->doUnwatch();
-                       }
-               }
-
-               # The talk page isn't in the regular link tables, so we need to update manually:
-               $talkns = $ns ^ 1; # talk -> normal; normal -> talk
-               $dbw->update( 'page',
-                       array( 'page_touched' => $dbw->timestamp($now) ),
-                       array( 'page_namespace' => $talkns,
-                              'page_title' => $ttl ),
-                       $fname );
-
-               # Update links, etc.
-               $this->editUpdates( $text, $summary, $isminor, $now, $revisionId );
-
-               # Clear caches
-               Article::onArticleCreate( $this->mTitle );
-
-               # Output a redirect back to the article
-               $this->doRedirect( $this->isRedirect( $text ) );
-
-               wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text,
-                       $summary, $isminor,
-                       $watchthis, NULL ) );
-               wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text,
-                       $summary, $isminor,
-                       $watchthis, NULL ) );
-               wfProfileOut( $fname );
-       }
-
        /**
         * @return string Complete article text, or null if error
         */
        function replaceSection($section, $text, $summary = '', $edittime = NULL) {
-               $fname = 'Article::replaceSection';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
                
                if( $section == '' ) {
                        // Whole-page edit; let the text through unmolested.
@@ -1256,132 +1145,280 @@ class Article {
                        }
                }
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
                return $text;
        }
 
        /**
-        * Change an existing article. Puts the previous version back into the old table, updates RC
-        * and all necessary caches, mostly via the deferred update array.
+        * @deprecated use Article::doEdit()
+        */
+       function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false ) {
+               $flags = EDIT_NEW | EDIT_DEFER_UPDATES |
+                       ( $isminor ? EDIT_MINOR : 0 ) |
+                       ( $suppressRC ? EDIT_SUPPRESS_RC : 0 );
+
+               # If this is a comment, add the summary as headline
+               if ( $comment && $summary != "" ) {
+                       $text = "== {$summary} ==\n\n".$text;
+               }
+               
+               $this->doEdit( $text, $summary, $flags );
+
+               $dbw =& wfGetDB( DB_MASTER );
+               if ($watchthis) {
+                       if (!$this->mTitle->userIsWatching()) {
+                               $dbw->begin();
+                               $this->doWatch();
+                               $dbw->commit();
+                       }
+               } else {
+                       if ( $this->mTitle->userIsWatching() ) {
+                               $dbw->begin();
+                               $this->doUnwatch();
+                               $dbw->commit();
+                       }
+               }
+               $this->doRedirect( $this->isRedirect( $text ) );
+       }
+
+       /**
+        * @deprecated use Article::doEdit()
+        */
+       function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
+               $flags = EDIT_UPDATE | EDIT_DEFER_UPDATES |
+                       ( $minor ? EDIT_MINOR : 0 ) |
+                       ( $forceBot ? EDIT_FORCE_BOT : 0 );
+
+               $good = $this->doEdit( $text, $summary, $flags );
+               if ( $good ) {
+                       $dbw =& wfGetDB( DB_MASTER );
+                       if ($watchthis) {
+                               if (!$this->mTitle->userIsWatching()) {
+                                       $dbw->begin();
+                                       $this->doWatch();
+                                       $dbw->commit();
+                               }
+                       } else {
+                               if ( $this->mTitle->userIsWatching() ) {
+                                       $dbw->begin();
+                                       $this->doUnwatch();
+                                       $dbw->commit();
+                               }
+                       }
+
+                       $this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
+               }
+               return $good;
+       }
+
+       /**
+        * Article::doEdit()
+        *
+        * Change an existing article or create a new article. Updates RC and all necessary caches, 
+        * optionally via the deferred update array.
         *
         * It is possible to call this function from a command-line script, but note that you should
         * first set $wgUser, and clean up $wgDeferredUpdates after each edit.
+        *
+        * $wgUser must be set before calling this function.
+        *
+        * @param string $text New text
+        * @param string $summary Edit summary
+        * @param integer $flags bitfield:
+        *      EDIT_NEW
+        *          Article is known or assumed to be non-existent, create a new one
+        *      EDIT_UPDATE
+        *          Article is known or assumed to be pre-existing, update it
+        *      EDIT_MINOR
+        *          Mark this edit minor, if the user is allowed to do so
+        *      EDIT_SUPPRESS_RC
+        *          Do not log the change in recentchanges
+        *      EDIT_FORCE_BOT
+        *          Mark the edit a "bot" edit regardless of user rights
+        *      EDIT_DEFER_UPDATES
+        *          Defer some of the updates until the end of index.php
+        * 
+        * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected. 
+        * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If 
+        * 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.
+        *
+        * @return bool success
         */
-       function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
-               global $wgUser, $wgDBtransactions, $wgUseSquid;
-               global $wgPostCommitUpdateList, $wgUseFileCache;
+       function doEdit( $text, $summary, $flags = 0 ) {
+               global $wgUser, $wgDBtransactions;
 
-               $fname = 'Article::updateArticle';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
                $good = true;
 
+               if ( !($flags & EDIT_NEW) && !($flags & EDIT_UPDATE) ) {
+                       $aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
+                       if ( $aid ) {
+                               $flags |= EDIT_UPDATE;
+                       } else {
+                               $flags |= EDIT_NEW;
+                       }
+               }
+
                if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text,
-                       &$summary, &$minor,
-                       &$watchthis, &$sectionanchor ) ) ) {
-                       wfDebug( "$fname: ArticleSave hook aborted save!\n" );
-                       wfProfileOut( $fname );
+                       &$summary, $flags & EDIT_MINOR,
+                       null, null, &$flags ) ) ) 
+               {
+                       wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
+                       wfProfileOut( __METHOD__ );
                        return false;
                }
 
-               $isminor = $minor && $wgUser->isAllowed('minoredit');
+               # Silently ignore EDIT_MINOR if not allowed
+               $isminor = ( $flags & EDIT_MINOR ) && $wgUser->isAllowed('minoredit');
+               $bot = $wgUser->isBot() || ( $flags & EDIT_FORCE_BOT );
 
                $text = $this->preSaveTransform( $text );
+
                $dbw =& wfGetDB( DB_MASTER );
                $now = wfTimestampNow();
+               
+               if ( $flags & EDIT_UPDATE ) {
+                       # Update article, but only if changed.
 
-               # Update article, but only if changed.
+                       # Make sure the revision is either completely inserted or not inserted at all
+                       if( !$wgDBtransactions ) {
+                               $userAbort = ignore_user_abort( true );
+                       }
 
-               # It's important that we either rollback or complete, otherwise an attacker could
-               # overwrite cur entries by sending precisely timed user aborts. Random bored users
-               # could conceivably have the same effect, especially if cur is locked for long periods.
-               if( !$wgDBtransactions ) {
-                       $userAbort = ignore_user_abort( true );
-               }
+                       $oldtext = $this->getContent();
+                       $oldsize = strlen( $oldtext );
+                       $newsize = strlen( $text );
+                       $lastRevision = 0;
+                       $revisionId = 0;
+
+                       if ( 0 != strcmp( $text, $oldtext ) ) {
+                               $this->mGoodAdjustment = (int)$this->isCountable( $text )
+                                 - (int)$this->isCountable( $oldtext );
+                               $this->mTotalAdjustment = 0;
+
+                               $lastRevision = $dbw->selectField(
+                                       'page', 'page_latest', array( 'page_id' => $this->getId() ) );
+
+                               if ( !$lastRevision ) {
+                                       # Article gone missing
+                                       wfDebug( __METHOD__.": EDIT_UPDATE specified but article doesn't exist\n" );
+                                       wfProfileOut( __METHOD__ );
+                                       return false;
+                               }
+                               
+                               $revision = new Revision( array(
+                                       'page'       => $this->getId(),
+                                       'comment'    => $summary,
+                                       'minor_edit' => $isminor,
+                                       'text'       => $text
+                                       ) );
+
+                               $dbw->begin();
+                               $revisionId = $revision->insertOn( $dbw );
+
+                               # Update page
+                               $ok = $this->updateRevisionOn( $dbw, $revision, $lastRevision );
+
+                               if( !$ok ) {
+                                       /* Belated edit conflict! Run away!! */
+                                       $good = false;
+                                       $dbw->rollback();
+                               } else {
+                                       # Update recentchanges
+                                       if( !( $flags & EDIT_SUPPRESS_RC ) ) {
+                                               $rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
+                                                       $lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
+                                                       $revisionId );
+                                                       
+                                               # Mark as patrolled if the user can do so and has it set in their options
+                                               if( $wgUser->isAllowed( 'patrol' ) && $wgUser->getOption( 'autopatrol' ) ) {
+                                                       RecentChange::markPatrolled( $rcid );
+                                               }
+                                       }
+                                       $dbw->commit();
+                               }
+                       } else {
+                               // Keep the same revision ID, but do some updates on it
+                               $revisionId = $this->getRevIdFetched();
+                               // Update page_touched, this is usually implicit in the page update
+                               // Other cache updates are done in onArticleEdit()
+                               $this->mTitle->invalidateCache();
+                       }
 
-               $oldtext = $this->getContent();
-               $oldsize = strlen( $oldtext );
-               $newsize = strlen( $text );
-               $lastRevision = 0;
-               $revisionId = 0;
+                       if( !$wgDBtransactions ) {
+                               ignore_user_abort( $userAbort );
+                       }
 
-               if ( 0 != strcmp( $text, $oldtext ) ) {
-                       $this->mGoodAdjustment = (int)$this->isCountable( $text )
-                         - (int)$this->isCountable( $oldtext );
-                       $this->mTotalAdjustment = 0;
-                       $now = wfTimestampNow();
+                       if ( $good ) {
+                               # 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 );
+                       }
+               } else {
+                       # Create new article
+                       
+                       # Set statistics members
+                       # We work out if it's countable after PST to avoid counter drift 
+                       # when articles are created with {{subst:}}
+                       $this->mGoodAdjustment = (int)$this->isCountable( $text );
+                       $this->mTotalAdjustment = 1;
 
-                       $lastRevision = $dbw->selectField(
-                               'page', 'page_latest', array( 'page_id' => $this->getId() ) );
+                       $dbw->begin();
 
+                       # Add the page record; stake our claim on this title!
+                       # This will fail with a database query exception if the article already exists
+                       $newid = $this->insertOn( $dbw );
+
+                       # Save the revision text...
                        $revision = new Revision( array(
-                               'page'       => $this->getId(),
+                               'page'       => $newid,
                                'comment'    => $summary,
                                'minor_edit' => $isminor,
                                'text'       => $text
                                ) );
-
-                       $dbw->begin();
                        $revisionId = $revision->insertOn( $dbw );
 
-                       # Update page
-                       $ok = $this->updateRevisionOn( $dbw, $revision, $lastRevision );
+                       $this->mTitle->resetArticleID( $newid );
 
-                       if( !$ok ) {
-                               /* Belated edit conflict! Run away!! */
-                               $good = false;
-                               $dbw->rollback();
-                       } else {
-                               # Update recentchanges
-                               $bot = (int)($wgUser->isBot() || $forceBot);
-                               $rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
-                                       $lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
-                                       $revisionId );
-                                       
-                               # Mark as patrolled if the user can do so and has it set in their options
+                       # Update the page record with revision data
+                       $this->updateRevisionOn( $dbw, $revision, 0 );
+
+                       if( !( $flags & EDIT_SUPPRESS_RC ) ) {
+                               $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
+                                 '', strlen( $text ), $revisionId );
+                               # Mark as patrolled if the user can and has the option set
                                if( $wgUser->isAllowed( 'patrol' ) && $wgUser->getOption( 'autopatrol' ) ) {
                                        RecentChange::markPatrolled( $rcid );
                                }
-                                       
-                               $dbw->commit();
                        }
-               } else {
-                       // Keep the same revision ID, but do some updates on it
-                       $revisionId = $this->getRevIdFetched();
-               }
+                       $dbw->commit();
 
-               if( !$wgDBtransactions ) {
-                       ignore_user_abort( $userAbort );
-               }
+                       # Update links, etc.
+                       $this->editUpdates( $text, $summary, $isminor, $now, $revisionId );
 
-               if ( $good ) {
-                       # Invalidate cache of this article and all pages using this article 
-                       # as a template. Partly deferred.
-                       Article::onArticleEdit( $this->mTitle );
-                       
-                       if ($watchthis) {
-                               if (!$this->mTitle->userIsWatching()) {
-                                       $dbw->begin();
-                                       $this->doWatch();
-                                       $dbw->commit();
-                               }
-                       } else {
-                               if ( $this->mTitle->userIsWatching() ) {
-                                       $dbw->begin();
-                                       $this->doUnwatch();
-                                       $dbw->commit();
-                               }
-                       }
-                       # Update links tables, site stats, etc.
-                       $this->editUpdates( $text, $summary, $minor, $now, $revisionId );
+                       # Clear caches
+                       Article::onArticleCreate( $this->mTitle );
 
-                       # Output a redirect back to the article
-                       $this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
+                       wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text,
+                               $summary, $flags & EDIT_MINOR,
+                               null, null, &$flags ) );
                }
+
+               if ( $good && !( $flags & EDIT_DEFER_UPDATES ) ) {
+                       wfDoUpdates();
+               }
+
                wfRunHooks( 'ArticleSaveComplete',
                        array( &$this, &$wgUser, $text,
-                       $summary, $minor,
-                       $watchthis, $sectionanchor ) );
-               wfProfileOut( $fname );
+                       $summary, $flags & EDIT_MINOR,
+                       null, null, &$flags ) );
+               
+               wfProfileOut( __METHOD__ );
                return $good;
        }
 
@@ -1651,7 +1688,6 @@ class Article {
         */
        function delete() {
                global $wgUser, $wgOut, $wgRequest;
-               $fname = 'Article::delete';
                $confirm = $wgRequest->wasPosted() &&
                        $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
                $reason = $wgRequest->getText( 'wpReason' );
@@ -1679,7 +1715,7 @@ class Article {
                # Better double-check that it hasn't been deleted yet!
                $dbw =& wfGetDB( DB_MASTER );
                $conds = $this->mTitle->pageCond();
-               $latest = $dbw->selectField( 'page', 'page_latest', $conds, $fname );
+               $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
                if ( $latest === false ) {
                        $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
                        return;
@@ -1769,8 +1805,7 @@ class Article {
         * @return array Array of authors, duplicates not removed
         */
        function getLastNAuthors( $num, $revLatest = 0 ) {
-               $fname = 'Article::getLastNAuthors';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                // First try the slave
                // If that doesn't have the latest revision, try the master
@@ -1783,13 +1818,13 @@ class Article {
                                        'page_namespace' => $this->mTitle->getNamespace(),
                                        'page_title' => $this->mTitle->getDBkey(),
                                        'rev_page = page_id'
-                               ), $fname, $this->getSelectOptions( array(
+                               ), __METHOD__, $this->getSelectOptions( array(
                                        'ORDER BY' => 'rev_timestamp DESC',
                                        'LIMIT' => $num
                                ) )
                        );
                        if ( !$res ) {
-                               wfProfileOut( $fname );
+                               wfProfileOut( __METHOD__ );
                                return array();
                        }
                        $row = $db->fetchObject( $res );
@@ -1805,7 +1840,7 @@ class Article {
                while ( $row = $db->fetchObject( $res ) ) {
                        $authors[] = $row->rev_user_text;
                }
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
                return $authors;
        }
        
@@ -1858,8 +1893,7 @@ class Article {
         */
        function doDelete( $reason ) {
                global $wgOut, $wgUser;
-               $fname = 'Article::doDelete';
-               wfDebug( $fname."\n" );
+               wfDebug( __METHOD__."\n" );
 
                if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
                        if ( $this->doDeleteArticle( $reason ) ) {
@@ -1889,8 +1923,7 @@ class Article {
                global $wgUseSquid, $wgDeferredUpdateList;
                global $wgPostCommitUpdateList, $wgUseTrackbacks;
 
-               $fname = 'Article::doDeleteArticle';
-               wfDebug( $fname."\n" );
+               wfDebug( __METHOD__."\n" );
 
                $dbw =& wfGetDB( DB_MASTER );
                $ns = $this->mTitle->getNamespace();
@@ -1928,18 +1961,18 @@ class Article {
                        ), array(
                                'page_id' => $id,
                                'page_id = rev_page'
-                       ), $fname
+                       ), __METHOD__
                );
 
                # Now that it's safely backed up, delete it
-               $dbw->delete( 'revision', array( 'rev_page' => $id ), $fname );
-               $dbw->delete( 'page', array( 'page_id' => $id ), $fname);
+               $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
+               $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
 
                if ($wgUseTrackbacks)
-                       $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), $fname );
+                       $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
 
                # Clean up recentchanges entries...
-               $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), $fname );
+               $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
 
                # Finally, clean up the link tables
                $t = $this->mTitle->getPrefixedDBkey();
@@ -1970,7 +2003,6 @@ class Article {
         */
        function rollback() {
                global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-               $fname = 'Article::rollback';
 
                if( $wgUser->isAllowed( 'rollback' ) ) {
                        if( $wgUser->isBlocked() ) {
@@ -2033,7 +2065,7 @@ class Article {
                        array(
                                'rev_page' => $current->getPage(),
                                "rev_user <> {$user} OR rev_user_text <> {$user_text}"
-                       ), $fname,
+                       ), __METHOD__,
                        array(
                                'USE INDEX' => 'page_timestamp',
                                'ORDER BY'  => 'rev_timestamp DESC' )
@@ -2061,7 +2093,7 @@ class Article {
                                        'rc_cur_id'    => $current->getPage(),
                                        'rc_user_text' => $current->getUserText(),
                                        "rc_timestamp > '{$s->rev_timestamp}'",
-                               ), $fname
+                               ), __METHOD__
                        );
                }
 
@@ -2113,8 +2145,7 @@ class Article {
        function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid) {
                global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser;
 
-               $fname = 'Article::editUpdates';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                # Parse the text
                $options = new ParserOptions;
@@ -2148,7 +2179,7 @@ class Article {
                $shortTitle = $this->mTitle->getDBkey();
 
                if ( 0 == $id ) {
-                       wfProfileOut( $fname );
+                       wfProfileOut( __METHOD__ );
                        return;
                }
 
@@ -2177,7 +2208,7 @@ class Article {
                        $wgMessageCache->replace( $shortTitle, $text );
                }
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
        }
 
        /**
@@ -2277,7 +2308,6 @@ class Article {
         *
         */
        function checkTouched() {
-               $fname = 'Article::checkTouched';
                if( !$this->mDataLoaded ) {
                        $this->loadPageData();
                }
@@ -2315,8 +2345,7 @@ class Article {
         * @param bool $minor whereas it's a minor modification
         */
        function quickEdit( $text, $comment = '', $minor = 0 ) {
-               $fname = 'Article::quickEdit';
-               wfProfileIn( $fname );
+               wfProfileIn( __METHOD__ );
 
                $dbw =& wfGetDB( DB_MASTER );
                $dbw->begin();
@@ -2331,7 +2360,7 @@ class Article {
                $this->updateRevisionOn( $dbw, $revision );
                $dbw->commit();
 
-               wfProfileOut( $fname );
+               wfProfileOut( __METHOD__ );
        }
 
        /**
@@ -2405,6 +2434,15 @@ class Article {
         */
 
        static function onArticleCreate($title) {
+               # The talk page isn't in the regular link tables, so we need to update manually:
+               if ( $title->isTalkPage() ) {
+                       $other = $title->getSubjectPage();
+               } else {
+                       $other = $title->getTalkPage();
+               }
+               $other->invalidateCache();
+               $other->purgeSquid();
+
                $title->touchLinks();
                $title->purgeSquid();
        }
@@ -2458,7 +2496,6 @@ class Article {
         */
        function info() {
                global $wgLang, $wgOut, $wgAllowPageInfo, $wgUser;
-               $fname = 'Article::info';
 
                if ( !$wgAllowPageInfo ) {
                        $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
@@ -2487,7 +2524,7 @@ class Article {
                                'watchlist',
                                'COUNT(*)',
                                $wl_clause,
-                               $fname,
+                               __METHOD__,
                                $this->getSelectOptions() );
 
                        $pageInfo = $this->pageCountInfo( $page );
@@ -2524,20 +2561,19 @@ class Article {
                $dbr =& wfGetDB( DB_SLAVE );
 
                $rev_clause = array( 'rev_page' => $id );
-               $fname = 'Article::pageCountInfo';
 
                $edits = $dbr->selectField(
                        'revision',
                        'COUNT(rev_page)',
                        $rev_clause,
-                       $fname,
+                       __METHOD__,
                        $this->getSelectOptions() );
 
                $authors = $dbr->selectField(
                        'revision',
                        'COUNT(DISTINCT rev_user_text)',
                        $rev_clause,
-                       $fname,
+                       __METHOD__,
                        $this->getSelectOptions() );
 
                return array( 'edits' => $edits, 'authors' => $authors );
index 4ae8696..e684625 100644 (file)
@@ -164,5 +164,15 @@ define( 'RC_LOG', 3);
 define( 'RC_MOVE_OVER_REDIRECT', 4);
 /**#@-*/
 
+/**#@+
+ * Article edit flags
+ */
+define( 'EDIT_NEW', 1 );
+define( 'EDIT_UPDATE', 2 );
+define( 'EDIT_MINOR', 4 ); 
+define( 'EDIT_SUPPRESS_RC', 8 );
+define( 'EDIT_FORCE_BOT', 16 );
+define( 'EDIT_DEFER_UPDATES', 32 );
+/**#@-*/
 
 ?>