moved rtl stylesheet to separate style section again, after all other styles to be...
[lhc/web/wiklou.git] / includes / Title.php
index d45d2c4..77b1a95 100644 (file)
@@ -23,7 +23,9 @@ class Title {
                               # Only null or "sysop" are supported
        var $mRestrictionsLoaded; # Boolean for initialisation on demand
        var $mPrefixedText;       # Text form including namespace/interwiki, initialised on demand
-
+       var $mDefaultNamespace;   # Namespace index when there is no namespace
+                              # Zero except in {{transclusion}} tags
+       
 #----------------------------------------------------------------------------
 #   Construction
 #----------------------------------------------------------------------------
@@ -36,6 +38,7 @@ class Title {
                $this->mNamespace = 0;
                $this->mRestrictionsLoaded = false;
                $this->mRestrictions = array();
+        $this->mDefaultNamespace = 0;
        }
 
        # From a prefixed DB key
@@ -50,24 +53,27 @@ class Title {
        }
        
        # From text, such as what you would find in a link
-       /* static */ function newFromText( $text )
+       /* static */ function newFromText( $text, $defaultNamespace = 0 )
        {       
                static $trans;
                $fname = "Title::newFromText";
                wfProfileIn( $fname );
-               
+
                # Note - mixing latin1 named entities and unicode numbered
                # ones will result in a bad link.
                if( !isset( $trans ) ) {
                        global $wgInputEncoding;
                        $trans = array_flip( get_html_translation_table( HTML_ENTITIES ) );
                        if( strcasecmp( "utf-8", $wgInputEncoding ) == 0 ) {
-                           $trans = array_map( "utf8_encode", $trans );
+                               $trans = array_map( "utf8_encode", $trans );
                        }
                }
 
+               if( is_object( $text ) ) {
+                       wfDebugDieBacktrace( "Called with object instead of string." );
+               }
                $text = strtr( $text, $trans );
-               
+
                $text = wfMungeToUtf8( $text );
                
                
@@ -76,7 +82,12 @@ class Title {
 
                $t = new Title();
                $t->mDbkeyform = str_replace( " ", "_", $text );
+               $t->mDefaultNamespace = $defaultNamespace;
+
                wfProfileOut( $fname );
+               if ( !is_object( $t ) ) {
+                       var_dump( debug_backtrace() );
+               }
                if( $t->secureAndSplit() ) {
                        return $t;
                } else {
@@ -97,16 +108,28 @@ class Title {
                
                # For links that came from outside, check for alternate/legacy
                # character encoding.
-               wfDebug( "Refer: {$_SERVER['HTTP_REFERER']}\n" );
                wfDebug( "Servr: $wgServer\n" );
                if( empty( $_SERVER["HTTP_REFERER"] ) ||
                        strncmp($wgServer, $_SERVER["HTTP_REFERER"], strlen( $wgServer ) ) ) 
                {
                        $s = $wgLang->checkTitleEncoding( $s );
+               } else {
+                       wfDebug( "Refer: {$_SERVER['HTTP_REFERER']}\n" );
                }
                
                $t->mDbkeyform = str_replace( " ", "_", $s );
                if( $t->secureAndSplit() ) {
+
+                       # check that lenght of title is < cur_title size
+                       $sql = "SHOW COLUMNS FROM cur LIKE \"cur_title\";";
+                       $cur_title_object = wfFetchObject(wfQuery( $sql, DB_READ ));
+
+                       preg_match( "/\((.*)\)/", $cur_title_object->Type, $cur_title_size);
+
+                       if (strlen($t->mDbkeyform) > $cur_title_size[1] ) {
+                               return NULL;
+                       }
+
                        return $t;
                } else {
                        return NULL;
@@ -215,7 +238,9 @@ class Title {
        # The URL contains $1, which is replaced by the title
        function getInterwikiLink( $key )
        {       
-               global $wgMemc, $wgDBname, $title_interwiki_cache;
+               global $wgMemc, $wgDBname;
+               static $title_interwiki_cache = array();
+
                $k = "$wgDBname:interwiki:$key";
 
                if( array_key_exists( $k, $title_interwiki_cache ) )
@@ -241,6 +266,32 @@ class Title {
                return $s->iw_url;
        }
        
+       # Update the cur_touched field for an array of title objects
+       # Inefficient unless the IDs are already loaded into the link cache
+       /* static */ function touchArray( $titles, $timestamp = "" ) {
+               if ( count( $titles ) == 0 ) {
+                       return;
+               }
+               if ( $timestamp == "" ) {
+                       $timestamp = wfTimestampNow();
+               }
+               $sql = "UPDATE cur SET cur_touched='{$timestamp}' WHERE cur_id IN (";
+               $first = true;
+
+               foreach ( $titles as $title ) {
+                       if ( ! $first ) { 
+                               $sql .= ","; 
+                       }
+
+                       $first = false;
+                       $sql .= $title->getArticleID();
+               }
+               $sql .= ")";
+               if ( ! $first ) {
+                       wfQuery( $sql, DB_WRITE, "Title::touchArray" );
+               }
+       }
+
 #----------------------------------------------------------------------------
 #      Other stuff
 #----------------------------------------------------------------------------
@@ -255,6 +306,7 @@ class Title {
        function setNamespace( $n ) { $this->mNamespace = $n; }
        function getInterwiki() { return $this->mInterwiki; }
        function getFragment() { return $this->mFragment; }
+       function getDefaultNamespace() { return $this->mDefaultNamespace; }
 
        # Get title for search index
        function getIndexTitle()
@@ -300,20 +352,21 @@ class Title {
        }
 
        # Get a real URL referring to this title, with interwiki link and fragment
-       function getFullURL()
+       function getFullURL( $query = "" )
        {
                global $wgLang, $wgArticlePath, $wgServer, $wgScript;
 
                if ( "" == $this->mInterwiki ) {
                        $p = $wgArticlePath;
-               } else {
-                       $p = $this->getInterwikiLink( $this->mInterwiki );
+                       return $wgServer . $this->getLocalUrl( $query );
                }
+               
+               $p = $this->getInterwikiLink( $this->mInterwiki );
                $n = $wgLang->getNsText( $this->mNamespace );
                if ( "" != $n ) { $n .= ":"; }
                $u = str_replace( "$1", $n . $this->mUrlform, $p );
                if ( "" != $this->mFragment ) {
-                       $u .= "#" . $this->mFragment;
+                       $u .= "#" . wfUrlencode( $this->mFragment );
                }
                return $u;
        }
@@ -324,9 +377,14 @@ class Title {
        # * Optionally adds the server and escapes for HTML
        # * Setting $query to "-" makes an old-style URL with nothing in the
        #   query except a title
-       function getURL( $query = "", $escape = false, $full = false )
+       
+       function getURL() {
+               die( "Call to obsolete obsolete function Title::getURL()" );
+       }
+       
+       function getLocalURL( $query = "" )
        {
-               global $wgLang, $wgArticlePath, $wgScript, $wgServer;
+               global $wgLang, $wgArticlePath, $wgScript;
                
                if ( $this->isExternal() ) {
                        return $this->getFullURL();
@@ -343,19 +401,26 @@ class Title {
                                $url = "{$wgScript}?title={$dbkey}&{$query}";
                        } else {
                                # Top level wiki
-                               $url = "/{$dbkey}&{$query}";
+                               $url = "/{$dbkey}?{$query}";
                        }
                }
-               
-               if ( $full ) {
-                       $url = $wgServer . $url;
-               }
-
-               if ( $escape ) {
-                       $url = wfEscapeHTML( $url );
-               }
                return $url;
        }
+       
+       function escapeLocalURL( $query = "" ) {
+               return wfEscapeHTML( $this->getLocalURL( $query ) );
+       }
+       
+       function escapeFullURL( $query = "" ) {
+               return wfEscapeHTML( $this->getFullURL( $query ) );
+       }
+       
+       function getInternalURL( $query = "" ) {
+               # Used in various Squid-related code, in case we have a different
+               # internal hostname for the server than the exposed one.
+               global $wgInternalServer;
+               return $wgInternalServer . $this->getLocalURL( $query );
+       }
 
        # Get the edit URL, or a null string if it is an interwiki link
        function getEditURL()
@@ -363,7 +428,7 @@ class Title {
                global $wgServer, $wgScript;
 
                if ( "" != $this->mInterwiki ) { return ""; }
-               $s = $this->getURL( "action=edit" );
+               $s = $this->getLocalURL( "action=edit" );
 
                return $s;
        }
@@ -518,7 +583,7 @@ class Title {
        #
        /* private */ function secureAndSplit()
        {
-               global $wgLang, $wgLocalInterwiki;
+               global $wgLang, $wgLocalInterwiki, $wgCapitalLinks;
                $fname = "Title::secureAndSplit";
                wfProfileIn( $fname );
                
@@ -531,20 +596,14 @@ class Title {
                        $rxTc = "/[^" . Title::legalChars() . "]/";
                }
 
-
                $this->mInterwiki = $this->mFragment = "";
-               $this->mNamespace = 0;
+               $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
 
                # Clean up whitespace
                #
                $t = preg_replace( "/[\\s_]+/", "_", $this->mDbkeyform );
-               if ( "_" == $t{0} ) { 
-                       $t = substr( $t, 1 ); 
-               }
-               $l = strlen( $t );
-               if ( $l && ( "_" == $t{$l-1} ) ) { 
-                       $t = substr( $t, 0, $l-1 ); 
-               }
+               $t = preg_replace( '/^_*(.*?)_*$/', '$1', $t );
+
                if ( "" == $t ) {
                        wfProfileOut( $fname );
                        return false;
@@ -558,15 +617,21 @@ class Title {
                        $t = substr( $t, 1 );
                }
 
-               # Redundant initial colon
+               # Initial colon indicating main namespace
                if ( ":" == $t{0} ) {
                        $r = substr( $t, 1 );
+                       $this->mNamespace = NS_MAIN;
                } else {
                        # Namespace or interwiki prefix
-                       if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z0-9_\\x80-\\xff]+):_*(.*)$/", $t, $m ) ) {
+                       if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z0-9_\\x80-\\xff]+?)_*:_*(.*)$/", $t, $m ) ) {
                                #$p = strtolower( $m[1] );
                                $p = $m[1];
-                               if ( $ns = $wgLang->getNsIndex( strtolower( $p ) )) {
+                               $lowerNs = strtolower( $p );
+                               if ( $ns = Namespace::getCanonicalIndex( $lowerNs ) ) {
+                                       # Canonical namespace
+                                       $t = $m[2];
+                                       $this->mNamespace = $ns;
+                               } elseif ( $ns = $wgLang->getNsIndex( $lowerNs )) {
                                        # Ordinary namespace
                                        $t = $m[2];
                                        $this->mNamespace = $ns;
@@ -612,7 +677,9 @@ class Title {
                }
 
                # Initial capital letter
-               if( $this->mInterwiki == "") $t = $wgLang->ucfirst( $r );
+               if( $wgCapitalLinks && $this->mInterwiki == "") {
+                       $t = $wgLang->ucfirst( $r );
+               }
                
                # Fill fields
                $this->mDbkeyform = $t;
@@ -633,5 +700,399 @@ class Title {
        function getSubjectPage() {
                return Title::makeTitle( Namespace::getSubject( $this->getNamespace() ), $this->getDBkey() );
        }
+
+       # Get an array of Title objects linking to this title
+       # Also stores the IDs in the link cache
+       function getLinksTo() {
+               global $wgLinkCache;
+               $id = $this->getArticleID();
+               $sql = "SELECT cur_namespace,cur_title,cur_id FROM cur,links WHERE l_from=cur_id AND l_to={$id}";
+               $res = wfQuery( $sql, DB_READ, "Title::getLinksTo" );
+               $retVal = array();
+               if ( wfNumRows( $res ) ) {
+                       while ( $row = wfFetchObject( $res ) ) {
+                               $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title );
+                               $wgLinkCache->addGoodLink( $row->cur_id, $titleObj->getPrefixedDBkey() );
+                               $retVal[] = $titleObj;
+                       }
+               }
+               wfFreeResult( $res );
+               return $retVal;
+       }
+
+       # Get an array of Title objects linking to this non-existent title
+       # Also stores the IDs in the link cache
+       function getBrokenLinksTo() {
+               global $wgLinkCache;
+               $encTitle = wfStrencode( $this->getPrefixedDBkey() );
+               $sql = "SELECT cur_namespace,cur_title,cur_id FROM brokenlinks,cur " .
+                 "WHERE bl_from=cur_id AND bl_to='$encTitle'";
+               $res = wfQuery( $sql, DB_READ, "Title::getBrokenLinksTo" );
+               $retVal = array();
+               if ( wfNumRows( $res ) ) {
+                       while ( $row = wfFetchObject( $res ) ) {
+                               $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title );
+                               $wgLinkCache->addGoodLink( $titleObj->getPrefixedDBkey(), $row->cur_id );
+                               $retVal[] = $titleObj;
+                       }
+               }
+               wfFreeResult( $res );
+               return $retVal;
+       }
+
+       function getSquidURLs() {
+               return array(
+                       $this->getInternalURL(),
+                       $this->getInternalURL( "action=history" )
+               );
+       }
+
+       function moveNoAuth( &$nt ) {
+               return $this->moveTo( $nt, false );
+       }
+       
+       # Move a title to a new location
+       # Returns true on success, message name on failure
+       # auth indicates whether wgUser's permissions should be checked
+       function moveTo( &$nt, $auth = true ) {
+               $fname = "Title::move";
+               $oldid = $this->getArticleID();
+               $newid = $nt->getArticleID();
+
+               if( !$this or !$nt ) {
+                       return "badtitletext";
+               }
+
+               if ( strlen( $nt->getDBkey() ) < 1 ) {
+                       return "articleexists";
+               }
+               if ( ( ! Namespace::isMovable( $this->getNamespace() ) ) ||
+                        ( "" == $this->getDBkey() ) ||
+                        ( "" != $this->getInterwiki() ) ||
+                        ( !$oldid ) ||
+                    ( ! Namespace::isMovable( $nt->getNamespace() ) ) ||
+                        ( "" == $nt->getDBkey() ) ||
+                        ( "" != $nt->getInterwiki() ) ) {
+                       return "badarticleerror";
+               }
+
+               if ( $auth && ( !$this->userCanEdit() || !$nt->userCanEdit() ) ) {
+                       return "protectedpage";
+               }
+               
+               # The move is allowed only if (1) the target doesn't exist, or
+               # (2) the target is a redirect to the source, and has no history
+               # (so we can undo bad moves right after they're done).
+
+               if ( 0 != $newid ) { # Target exists; check for validity
+                       if ( ! $this->isValidMoveTarget( $nt ) ) {
+                               return "articleexists";
+                       }
+                       $this->moveOverExistingRedirect( $nt );
+               } else { # Target didn't exist, do normal move.
+                       $this->moveToNewTitle( $nt, $newid );
+               }
+
+               # Update watchlists
+               
+               $oldnamespace = $this->getNamespace() & ~1;
+               $newnamespace = $nt->getNamespace() & ~1;
+               $oldtitle = $this->getDBkey();
+               $newtitle = $nt->getDBkey();
+
+               if( $oldnamespace != $newnamespace && $oldtitle != $newtitle ) {
+                       WatchedItem::duplicateEntries( $this, $nt );
+               }
+
+               # Update search engine
+               $u = new SearchUpdate( $oldid, $nt->getPrefixedDBkey() );
+               $u->doUpdate();
+               $u = new SearchUpdate( $newid, $this->getPrefixedDBkey(), "" );
+               $u->doUpdate();
+
+               return true;
+       }
+       
+       # Move page to title which is presently a redirect to the source page
+       
+       /* private */ function moveOverExistingRedirect( &$nt )
+       {
+               global $wgUser, $wgLinkCache, $wgUseSquid, $wgMwRedir;
+               $fname = "Title::moveOverExistingRedirect";
+               $comment = wfMsg( "1movedto2", $this->getPrefixedText(), $nt->getPrefixedText() );
+               
+        $now = wfTimestampNow();
+        $won = wfInvertTimestamp( $now );
+               $newid = $nt->getArticleID();
+               $oldid = $this->getArticleID();
+               
+               # Change the name of the target page:
+               wfUpdateArray( 
+                       /* table */ 'cur',
+                       /* SET */ array( 
+                               'cur_touched' => $now, 
+                               'cur_namespace' => $nt->getNamespace(),
+                               'cur_title' => $nt->getDBkey()
+                       ), 
+                       /* WHERE */ array( 'cur_id' => $oldid ),
+                       $fname
+               );
+               $wgLinkCache->clearLink( $nt->getPrefixedDBkey() );
+
+               # Repurpose the old redirect. We don't save it to history since
+               # by definition if we've got here it's rather uninteresting.
+               
+               $redirectText = $wgMwRedir->getSynonym( 0 ) . " [[" . $nt->getPrefixedText() . "]]\n";
+               wfUpdateArray( 
+                       /* table */ 'cur',
+                       /* SET */ array(
+                               'cur_touched' => $now,
+                               'cur_timestamp' => $now,
+                               'inverse_timestamp' => $won,
+                               'cur_namespace' => $this->getNamespace(),
+                               'cur_title' => $this->getDBkey(),
+                               'cur_text' => $wgMwRedir->getSynonym( 0 ) . " [[" . $nt->getPrefixedText() . "]]\n",
+                               'cur_comment' => $comment,
+                               'cur_user' => $wgUser->getID(),
+                               'cur_minor_edit' => 0,
+                               'cur_counter' => 0,
+                               'cur_restrictions' => '',
+                               'cur_user_text' => $wgUser->getName(),
+                               'cur_is_redirect' => 1,
+                               'cur_is_new' => 1
+                       ),
+                       /* WHERE */ array( 'cur_id' => $newid ),
+                       $fname
+               );
+               
+               $wgLinkCache->clearLink( $this->getPrefixedDBkey() );
+
+               # Fix the redundant names for the past revisions of the target page.
+               # The redirect should have no old revisions.
+               wfUpdateArray(
+                       /* table */ 'old',
+                       /* SET */ array( 
+                               'old_namespace' => $nt->getNamespace(),
+                               'old_title' => $nt->getDBkey(),
+                       ),
+                       /* WHERE */ array( 
+                               'old_namespace' => $this->getNamespace(),
+                               'old_title' => $this->getDBkey(),
+                       ),
+                       $fname
+               );
+               
+               RecentChange::notifyMove( $now, $this, $nt, $wgUser, $comment );
+
+               # Swap links
+               
+               # Load titles and IDs
+               $linksToOld = $this->getLinksTo();
+               $linksToNew = $nt->getLinksTo();
+               
+               # Make function to convert Titles to IDs
+               $titleToID = create_function('$t', 'return $t->getArticleID();');
+
+               # Reassign links to old title
+               if ( count( $linksToOld ) ) {
+                       $sql = "UPDATE links SET l_to=$newid WHERE l_from IN (";
+                       $sql .= implode( ",", array_map( $titleToID, $linksToOld ) );
+                       $sql .= ")";
+                       wfQuery( $sql, DB_WRITE, $fname );
+               }
+               
+               # Reassign links to new title
+               if ( count( $linksToNew ) ) {
+                       $sql = "UPDATE links SET l_to=$oldid WHERE l_from IN (";
+                       $sql .= implode( ",", array_map( $titleToID, $linksToNew ) );
+                       $sql .= ")";
+                       wfQuery( $sql, DB_WRITE, $fname );
+               }
+
+               # Note: the insert below must be after the updates above!
+
+               # Now, we record the link from the redirect to the new title.
+               # It should have no other outgoing links...
+               $sql = "DELETE FROM links WHERE l_from={$newid}";
+               wfQuery( $sql, DB_WRITE, $fname );
+               $sql = "INSERT INTO links (l_from,l_to) VALUES ({$newid},{$oldid})";
+               wfQuery( $sql, DB_WRITE, $fname );
+
+               # Purge squid
+               if ( $wgUseSquid ) {
+                       $urls = array_merge( $nt->getSquidURLs(), $this->getSquidURLs() );
+                       $u = new SquidUpdate( $urls );
+                       $u->doUpdate();
+               }
+       }
+
+       # Move page to non-existing title.
+       # Sets $newid to be the new article ID
+
+       /* private */ function moveToNewTitle( &$nt, &$newid )
+       {
+               global $wgUser, $wgLinkCache, $wgUseSquid;
+               $fname = "MovePageForm::moveToNewTitle";
+               $comment = wfMsg( "1movedto2", $this->getPrefixedText(), $nt->getPrefixedText() );
+
+               $now = wfTimestampNow();
+               $won = wfInvertTimestamp( $now );
+               $newid = $nt->getArticleID();
+               $oldid = $this->getArticleID();
+
+               # Rename cur entry
+               wfUpdateArray(
+                       /* table */ 'cur',
+                       /* SET */ array(
+                               'cur_touched' => $now,
+                               'cur_namespace' => $nt->getNamespace(),
+                               'cur_title' => $nt->getDBkey()
+                       ),
+                       /* WHERE */ array( 'cur_id' => $oldid ),
+                       $fname
+               );
+               
+               $wgLinkCache->clearLink( $nt->getPrefixedDBkey() );
+
+               # Insert redirct
+               wfInsertArray( 'cur', array(
+                       'cur_namespace' => $this->getNamespace(),
+                       'cur_title' => $this->getDBkey(),
+                       'cur_comment' => $comment,
+                       'cur_user' => $wgUser->getID(),
+                       'cur_user_text' => $wgUser->getName(),
+                       'cur_timestamp' => $now,
+                       'inverse_timestamp' => $won,
+                       'cur_touched' => $now,
+                       'cur_is_redirect' => 1,
+                       'cur_is_new' => 1,
+                       'cur_text' => "#REDIRECT [[" . $nt->getPrefixedText() . "]]\n" )
+               );
+               $newid = wfInsertId();
+               $wgLinkCache->clearLink( $this->getPrefixedDBkey() );
+
+               # Rename old entries
+               wfUpdateArray( 
+                       /* table */ 'old',
+                       /* SET */ array(
+                               'old_namespace' => $nt->getNamespace(),
+                               'old_title' => $nt->getDBkey()
+                       ),
+                       /* WHERE */ array(
+                               'old_namespace' => $this->getNamespace(),
+                               'old_title' => $this->getDBkey()
+                       ), $fname
+               );
+               
+               # Miscellaneous updates
+
+               RecentChange::notifyMove( $now, $this, $nt, $wgUser, $comment );
+               Article::onArticleCreate( $nt );
+
+               # Any text links to the old title must be reassigned to the redirect
+               $sql = "UPDATE links SET l_to={$newid} WHERE l_to={$oldid}";
+               wfQuery( $sql, DB_WRITE, $fname );
+
+               # Record the just-created redirect's linking to the page
+               $sql = "INSERT INTO links (l_from,l_to) VALUES ({$newid},{$oldid})";
+               wfQuery( $sql, DB_WRITE, $fname );
+
+               # Non-existent target may have had broken links to it; these must
+               # now be removed and made into good links.
+               $update = new LinksUpdate( $oldid, $nt->getPrefixedDBkey() );
+               $update->fixBrokenLinks();
+
+               # Purge old title from squid
+               # The new title, and links to the new title, are purged in Article::onArticleCreate()
+               $titles = $nt->getLinksTo();
+               if ( $wgUseSquid ) {
+                       $urls = $this->getSquidURLs();
+                       foreach ( $titles as $linkTitle ) {
+                               $urls[] = $linkTitle->getInternalURL();
+                       }
+                       $u = new SquidUpdate( $urls );
+                       $u->doUpdate();
+               }
+       }
+
+       # Checks if $this can be moved to $nt
+       # Both titles must exist in the database, otherwise it will blow up
+       function isValidMoveTarget( $nt )
+       {
+               $fname = "Title::isValidMoveTarget";
+
+               # Is it a redirect?
+               $id  = $nt->getArticleID();
+               $sql = "SELECT cur_is_redirect,cur_text FROM cur " .
+                 "WHERE cur_id={$id}";
+               $res = wfQuery( $sql, DB_READ, $fname );
+               $obj = wfFetchObject( $res );
+
+               if ( 0 == $obj->cur_is_redirect ) { 
+                       # Not a redirect
+                       return false; 
+               }
+
+               # Does the redirect point to the source?
+               if ( preg_match( "/\\[\\[\\s*([^\\]]*)]]/", $obj->cur_text, $m ) ) {
+                       $redirTitle = Title::newFromText( $m[1] );
+                       if ( 0 != strcmp( $redirTitle->getPrefixedDBkey(), $this->getPrefixedDBkey() ) ) {
+                               return false;
+                       }
+               }
+
+               # Does the article have a history?
+               $row = wfGetArray( 'old', array( 'old_id' ), array( 
+                       'old_namespace' => $nt->getNamespace(),
+                       'old_title' => $nt->getDBkey() )
+               );
+
+               # Return true if there was no history
+               return $row === false;
+       }
+       
+       # Create a redirect, fails if the title already exists, does not notify RC
+       # Returns success
+       function createRedirect( $dest, $comment ) {
+               global $wgUser;
+               if ( $this->getArticleID() ) {
+                       return false;
+               }
+               
+               $now = wfTimestampNow();
+               $won = wfInvertTimestamp( $now );
+
+               wfInsertArray( 'cur', array(
+                       'cur_namespace' => $this->getNamespace(),
+                       'cur_title' => $this->getDBkey(),
+                       'cur_comment' => $comment,
+                       'cur_user' => $wgUser->getID(),
+                       'cur_user_text' => $wgUser->getName(),
+                       'cur_timestamp' => $now,
+                       'inverse_timestamp' => $won,
+                       'cur_touched' => $now,
+                       'cur_is_redirect' => 1,
+                       'cur_is_new' => 1,
+                       'cur_text' => "#REDIRECT [[" . $dest->getPrefixedText() . "]]\n" 
+               ));
+               $newid = wfInsertId();
+               $this->resetArticleID( $newid );
+               
+               # Link table
+               if ( $dest->getArticleID() ) {
+                       wfInsertArray( 'links', array(
+                               'l_to' => $dest->getArticleID(),
+                               'l_from' => $newid
+                       ));
+               } else {
+                       wfInsertArray( 'brokenlinks', array( 
+                               'bl_to' => $dest->getPrefixedDBkey(),
+                               'bl_from' => $newid
+                       ));
+               }
+
+               Article::onArticleCreate( $this );
+               return true;
+       }
 }
 ?>