New global config setting $wgMaxTocLevel: Maximum indent level of toc.
[lhc/web/wiklou.git] / includes / Title.php
index 2813a75..b2f39bc 100644 (file)
@@ -109,14 +109,10 @@ class Title {
                
                $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] ) {
+                       # check that length of title is < cur_title size
+                       $dbr =& wfGetDB( DB_SLAVE );
+                       $maxSize = $dbr->textFieldSize( 'cur', 'cur_title' );
+                       if ( $maxSize != -1 && strlen( $t->mDbkeyform ) > $maxSize ) {
                                return NULL;
                        }
 
@@ -132,7 +128,8 @@ class Title {
        /* static */ function newFromID( $id ) 
        {
                $fname = "Title::newFromID";
-               $row = wfGetArray( "cur", array( "cur_namespace", "cur_title" ), 
+               $dbr =& wfGetDB( DB_SLAVE );
+               $row = $dbr->getArray( "cur", array( "cur_namespace", "cur_title" ), 
                        array( "cur_id" => $id ), $fname );
                if ( $row !== false ) {
                        $title = Title::makeTitle( $row->cur_namespace, $row->cur_title );
@@ -166,12 +163,12 @@ class Title {
        # Get the prefixed DB key associated with an ID
        /* static */ function nameOf( $id )
        {
-               $sql = "SELECT cur_namespace,cur_title FROM cur WHERE " .
-                 "cur_id={$id}";
-               $res = wfQuery( $sql, DB_READ, "Article::nameOf" );
-               if ( 0 == wfNumRows( $res ) ) { return NULL; }
+               $fname = 'Title::nameOf';
+               $dbr =& wfGetDB( DB_SLAVE );
+               
+               $s = $dbr->getArray( 'cur', array( 'cur_namespace','cur_title' ),  array( 'cur_id' => $id ), $fname );
+               if ( $s === false ) { return NULL; }
 
-               $s = wfFetchObject( $res );
                $n = Title::makeName( $s->cur_namespace, $s->cur_title );
                return $n;
        }
@@ -182,12 +179,20 @@ class Title {
                # Missing characters:
                #  * []|# Needed for link syntax
                #  * % and + are corrupted by Apache when they appear in the path
-               #  * % seems to work though
-               # 
+               #
+               # % seems to work though
+               #
+               # The problem with % is that URLs are double-unescaped: once by Apache's 
+               # path conversion code, and again by PHP. So %253F, for example, becomes "?".
+               # Our code does not double-escape to compensate for this, indeed double escaping
+               # would break if the double-escaped title was passed in the query string
+               # rather than the path. This is a minor security issue because articles can be
+               # created such that they are hard to view or edit. -- TS
+               #
                # Theoretically 0x80-0x9F of ISO 8859-1 should be disallowed, but
                # this breaks interlanguage links
                
-               $set = " %!\"$&'()*,\\-.\\/0-9:;<=>?@A-Z\\\\^_`a-z{}~\\x80-\\xFF";
+               $set = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z{}~\\x80-\\xFF";
                return $set;
        }
        
@@ -229,9 +234,8 @@ class Title {
        # The URL contains $1, which is replaced by the title
        function getInterwikiLink( $key )
        {       
-               global $wgMemc, $wgDBname, $wgInterwikiExpiry;
-               static $wgTitleInterwikiCache = array();
-
+               global $wgMemc, $wgDBname, $wgInterwikiExpiry, $wgTitleInterwikiCache;
+               $fname = 'Title::getInterwikiLink';
                $k = "$wgDBname:interwiki:$key";
 
                if( array_key_exists( $k, $wgTitleInterwikiCache ) )
@@ -243,15 +247,16 @@ class Title {
                        $wgTitleInterwikiCache[$k] = $s;
                        return $s->iw_url;
                }
-               $dkey = wfStrencode( $key );
-               $query = "SELECT iw_url,iw_local FROM interwiki WHERE iw_prefix='$dkey'";
-               $res = wfQuery( $query, DB_READ, "Title::getInterwikiLink" );
-               if(!$res) return "";
+               $dbr =& wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'interwiki', array( 'iw_url', 'iw_local' ), array( 'iw_prefix' => $key ), $fname );
+                if(!$res) return "";
                
-               $s = wfFetchObject( $res );
+               $s = $dbr->fetchObject( $res );
                if(!$s) {
+                       # Cache non-existence: create a blank object and save it to memcached
                        $s = (object)false;
                        $s->iw_url = "";
+                       $s->iw_local = 0;
                }
                $wgMemc->set( $k, $s, $wgInterwikiExpiry );
                $wgTitleInterwikiCache[$k] = $s;
@@ -280,20 +285,21 @@ class Title {
                if ( $timestamp == "" ) {
                        $timestamp = wfTimestampNow();
                }
-               $sql = "UPDATE cur SET cur_touched='{$timestamp}' WHERE cur_id IN (";
+               $dbw =& wfGetDB( DB_MASTER );
+               $cur = $dbw->tableName( 'cur' );
+               $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" );
+                       $dbw->query( $sql, "Title::touchArray" );
                }
        }
 
@@ -370,9 +376,6 @@ class Title {
                $n = $wgLang->getNsText( $this->mNamespace );
                if ( "" != $n ) { $n .= ":"; }
                $u = str_replace( "$1", $n . $this->mUrlform, $p );
-               if ( "" != $this->mFragment ) {
-                       $u .= "#" . wfUrlencode( $this->mFragment );
-               }
                return $u;
        }
 
@@ -487,15 +490,20 @@ class Title {
        # Can $wgUser edit this page?
        function userCanEdit()
        {
-
+               global $wgUser;
                if ( -1 == $this->mNamespace ) { return false; }
+               if ( NS_MEDIAWIKI == $this->mNamespace && !$wgUser->isSysop() ) { return false; }
                # if ( 0 == $this->getArticleID() ) { return false; }
                if ( $this->mDbkeyform == "_" ) { return false; }
+               # protect global styles and js
+               if ( NS_MEDIAWIKI == $this->mNamespace 
+                    && preg_match("/\\.(css|js)$/", $this->mTextform )
+                    && !$wgUser->isSysop() )
+               { return false; }
                //if ( $this->isCssJsSubpage() and !$this->userCanEditCssJsSubpage() ) { return false; }
                # protect css/js subpages of user pages
                # XXX: this might be better using restrictions
                # XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
-               global $wgUser;
                if( Namespace::getUser() == $this->mNamespace
                        and preg_match("/\\.(css|js)$/", $this->mTextform )
                        and !$wgUser->isSysop()
@@ -521,7 +529,7 @@ class Title {
                if( in_array( $name, $wgWhitelistRead ) ) return true;
                
                # Compatibility with old settings
-               if( $this->getNamespace() == NS_ARTICLE ) {
+               if( $this->getNamespace() == NS_MAIN ) {
                        if( in_array( ":" . $name, $wgWhitelistRead ) ) return true;
                }
                return false;
@@ -550,7 +558,8 @@ class Title {
                if ( 0 == $id ) { return array(); }
 
                if ( ! $this->mRestrictionsLoaded ) {
-                       $res = wfGetSQL( "cur", "cur_restrictions", "cur_id=$id" );
+                       $dbr =& wfGetDB( DB_SLAVE );
+                       $res = $dbr->getField( "cur", "cur_restrictions", "cur_id=$id" );
                        $this->mRestrictions = explode( ",", trim( $res ) );
                        $this->mRestrictionsLoaded = true;
                }
@@ -558,15 +567,13 @@ class Title {
        }
        
        # Is there a version of this page in the deletion archive?
+       # Returns the number of archived revisions
        function isDeleted() {
-               $ns = $this->getNamespace();
-               $t = wfStrencode( $this->getDBkey() );
-               $sql = "SELECT COUNT(*) AS n FROM archive WHERE ar_namespace=$ns AND ar_title='$t'";
-               if( $res = wfQuery( $sql, DB_READ ) ) {
-                       $s = wfFetchObject( $res );
-                       return $s->n;
-               }
-               return 0;
+               $fname = 'Title::isDeleted';
+               $dbr =& wfGetDB( DB_SLAVE );
+               $n = $dbr->getField( 'archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(), 
+                       'ar_title' => $this->getDBkey() ), $fname );
+               return (int)$n;
        }
 
        # Get the article ID from the link cache
@@ -598,10 +605,16 @@ class Title {
        # Called from LinksUpdate.php
        function invalidateCache() {
                $now = wfTimestampNow();
-               $ns = $this->getNamespace();
-               $ti = wfStrencode( $this->getDBkey() );
-               $sql = "UPDATE cur SET cur_touched='$now' WHERE cur_namespace=$ns AND cur_title='$ti'";
-               return wfQuery( $sql, DB_WRITE, "Title::invalidateCache" );
+               $dbw =& wfGetDB( DB_MASTER );
+               $success = $dbw->updateArray( 'cur', 
+                       array( /* SET */ 
+                               'cur_touched' => wfTimestampNow()
+                       ), array( /* WHERE */ 
+                               'cur_namespace' => $this->getNamespace() ,
+                               'cur_title' => $this->getDBkey()
+                       ), 'Title::invalidateCache'
+               );
+               return $success;
        }
 
        # Prefixes some arbitrary text with the namespace or interwiki prefix of this object
@@ -622,8 +635,8 @@ class Title {
        # Secure and split - main initialisation function for this object
        # 
        # Assumes that mDbkeyform has been set, and is urldecoded
-    # and uses undersocres, but not otherwise munged.  This function
-    # removes illegal characters, splits off the winterwiki and
+    # and uses underscores, but not otherwise munged.  This function
+    # removes illegal characters, splits off the interwiki and
     # namespace prefixes, sets the other forms, and canonicalizes
     # everything.      
        #
@@ -670,7 +683,7 @@ class Title {
                        $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( "/^(.+?)_*:_*(.*)$/", $t, $m ) ) {
                                #$p = strtolower( $m[1] );
                                $p = $m[1];
                                $lowerNs = strtolower( $p );
@@ -715,11 +728,18 @@ class Title {
                # Reject illegal characters.
                #
                if( preg_match( $rxTc, $r ) ) {
+                       wfProfileOut( $fname );
                        return false;
                }
                
                # "." and ".." conflict with the directories of those namesa
-               if ( $r === "." || $r === ".." || strpos( $r, "./" ) !== false ) {
+               if ( strpos( $r, "." ) !== false &&
+                    ( $r === "." || $r === ".." ||
+                      strpos( $r, "./" ) === 0 ||
+                      strpos( $r, "/./" !== false ) ||
+                      strpos( $r, "/../" !== false ) ) )
+               {
+                       wfProfileOut( $fname );
                        return false;
                }
 
@@ -750,41 +770,60 @@ class Title {
 
        # Get an array of Title objects linking to this title
        # Also stores the IDs in the link cache
-       function getLinksTo() {
+       # $options may be FOR UPDATE
+       function getLinksTo( $options = '' ) {
                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" );
+               
+               if ( $options ) {
+                       $db =& wfGetDB( DB_MASTER );
+               } else {
+                       $db =& wfGetDB( DB_SLAVE );
+               }
+               $cur = $db->tableName( 'cur' );
+               $links = $db->tableName( 'links' );
+
+               $sql = "SELECT cur_namespace,cur_title,cur_id FROM $cur,$links WHERE l_from=cur_id AND l_to={$id} $options";
+               $res = $db->query( $sql, "Title::getLinksTo" );
                $retVal = array();
-               if ( wfNumRows( $res ) ) {
-                       while ( $row = wfFetchObject( $res ) ) {
+               if ( $db->numRows( $res ) ) {
+                       while ( $row = $db->fetchObject( $res ) ) {
                                if ( $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title ) ) {
                                        $wgLinkCache->addGoodLink( $row->cur_id, $titleObj->getPrefixedDBkey() );
                                        $retVal[] = $titleObj;
                                }
                        }
                }
-               wfFreeResult( $res );
+               $db->freeResult( $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() {
+       function getBrokenLinksTo( $options = '' ) {
                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" );
+               
+               if ( $options ) {
+                       $db =& wfGetDB( DB_MASTER );
+               } else {
+                       $db =& wfGetDB( DB_SLAVE );
+               }
+               $cur = $db->tableName( 'cur' );
+               $brokenlinks = $db->tableName( 'brokenlinks' );
+               $encTitle = $db->strencode( $this->getPrefixedDBkey() );
+
+               $sql = "SELECT cur_namespace,cur_title,cur_id FROM $brokenlinks,$cur " .
+                 "WHERE bl_from=cur_id AND bl_to='$encTitle' $options";
+               $res = $db->query( $sql, "Title::getBrokenLinksTo" );
                $retVal = array();
-               if ( wfNumRows( $res ) ) {
-                       while ( $row = wfFetchObject( $res ) ) {
+               if ( $db->numRows( $res ) ) {
+                       while ( $row = $db->fetchObject( $res ) ) {
                                $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title );
                                $wgLinkCache->addGoodLink( $row->cur_id, $titleObj->getPrefixedDBkey() );
                                $retVal[] = $titleObj;
                        }
                }
-               wfFreeResult( $res );
+               $db->freeResult( $res );
                return $retVal;
        }
 
@@ -873,10 +912,11 @@ class Title {
         $won = wfInvertTimestamp( $now );
                $newid = $nt->getArticleID();
                $oldid = $this->getArticleID();
-               
+               $dbw =& wfGetDB( DB_MASTER );
+               $links = $dbw->tableName( 'links' );
+
                # Change the name of the target page:
-               wfUpdateArray( 
-                       /* table */ 'cur',
+               $dbw->updateArray( 'cur',
                        /* SET */ array( 
                                'cur_touched' => $now, 
                                'cur_namespace' => $nt->getNamespace(),
@@ -891,8 +931,7 @@ class Title {
                # by definition if we've got here it's rather uninteresting.
                
                $redirectText = $wgMwRedir->getSynonym( 0 ) . " [[" . $nt->getPrefixedText() . "]]\n";
-               wfUpdateArray( 
-                       /* table */ 'cur',
+               $dbw->updateArray( 'cur',
                        /* SET */ array(
                                'cur_touched' => $now,
                                'cur_timestamp' => $now,
@@ -917,7 +956,7 @@ class Title {
 
                # Fix the redundant names for the past revisions of the target page.
                # The redirect should have no old revisions.
-               wfUpdateArray(
+               $dbw->updateArray(
                        /* table */ 'old',
                        /* SET */ array( 
                                'old_namespace' => $nt->getNamespace(),
@@ -930,21 +969,21 @@ class Title {
                        $fname
                );
                
-               RecentChange::notifyMove( $now, $this, $nt, $wgUser, $comment );
+               RecentChange::notifyMoveOverRedirect( $now, $this, $nt, $wgUser, $comment );
 
                # Swap links
                
                # Load titles and IDs
-               $linksToOld = $this->getLinksTo();
-               $linksToNew = $nt->getLinksTo();
+               $linksToOld = $this->getLinksTo( 'FOR UPDATE' );
+               $linksToNew = $nt->getLinksTo( 'FOR UPDATE' );
                
                # Delete them all
-               $sql = "DELETE FROM links WHERE l_to=$oldid OR l_to=$newid";
-               wfQuery( $sql, DB_WRITE, $fname );
+               $sql = "DELETE FROM $links WHERE l_to=$oldid OR l_to=$newid";
+               $dbw->query( $sql, $fname );
 
                # Reinsert
                if ( count( $linksToOld ) || count( $linksToNew )) {
-                       $sql = "INSERT INTO links (l_from,l_to) VALUES ";
+                       $sql = "INSERT INTO $links (l_from,l_to) VALUES ";
                        $first = true;
 
                        # Insert links to old title
@@ -969,16 +1008,18 @@ class Title {
                                $sql .= "($id, $oldid)";
                        }
 
-                       wfQuery( $sql, DB_WRITE, $fname );
+                       $dbw->query( $sql, DB_MASTER, $fname );
                }
 
                # 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 );
-
+               $dbw->delete( 'links', array( 'l_from' => $newid ) );
+               $dbw->insertArray( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ) );
+               
+               # Clear linkscc
+               LinkCache::linksccClearLinksTo( $oldid );
+               LinkCache::linksccClearLinksTo( $newid );
+               
                # Purge squid
                if ( $wgUseSquid ) {
                        $urls = array_merge( $nt->getSquidURLs(), $this->getSquidURLs() );
@@ -1000,10 +1041,10 @@ class Title {
                $won = wfInvertTimestamp( $now );
                $newid = $nt->getArticleID();
                $oldid = $this->getArticleID();
+               $dbw =& wfGetDB( DB_MASTER );
 
                # Rename cur entry
-               wfUpdateArray(
-                       /* table */ 'cur',
+               $dbw->updateArray( 'cur',
                        /* SET */ array(
                                'cur_touched' => $now,
                                'cur_namespace' => $nt->getNamespace(),
@@ -1016,7 +1057,7 @@ class Title {
                $wgLinkCache->clearLink( $nt->getPrefixedDBkey() );
 
                # Insert redirct
-               wfInsertArray( 'cur', array(
+               $dbw->insertArray( 'cur', array(
                        'cur_namespace' => $this->getNamespace(),
                        'cur_title' => $this->getDBkey(),
                        'cur_comment' => $comment,
@@ -1027,13 +1068,13 @@ class Title {
                        'cur_touched' => $now,
                        'cur_is_redirect' => 1,
                        'cur_is_new' => 1,
-                       'cur_text' => "#REDIRECT [[" . $nt->getPrefixedText() . "]]\n" )
+                       'cur_text' => "#REDIRECT [[" . $nt->getPrefixedText() . "]]\n" ), $fname
                );
-               $newid = wfInsertId();
+               $newid = $dbw->insertId();
                $wgLinkCache->clearLink( $this->getPrefixedDBkey() );
 
                # Rename old entries
-               wfUpdateArray( 
+               $dbw->updateArray( 
                        /* table */ 'old',
                        /* SET */ array(
                                'old_namespace' => $nt->getNamespace(),
@@ -1045,18 +1086,18 @@ class Title {
                        ), $fname
                );
                
-               # Miscellaneous updates
+               # Record in RC
+               RecentChange::notifyMoveToNew( $now, $this, $nt, $wgUser, $comment );
 
-               RecentChange::notifyMove( $now, $this, $nt, $wgUser, $comment );
+               # Purge squid and linkscc as per article creation
                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 );
+               $dbw->updateArray( 'links', array( 'l_to' => $newid ), array( 'l_to' => $oldid ), $fname );
+               LinkCache::linksccClearLinksTo( $oldid );
 
                # 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 );
+               $dbw->insertArray( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ), $fname );
 
                # Non-existent target may have had broken links to it; these must
                # now be removed and made into good links.
@@ -1077,19 +1118,18 @@ class Title {
        }
 
        # Checks if $this can be moved to $nt
-       # Both titles must exist in the database, otherwise it will blow up
+       # Selects for update, so don't call it unless you mean business
        function isValidMoveTarget( $nt )
        {
                $fname = "Title::isValidMoveTarget";
+               $dbw =& wfGetDB( DB_MASTER );
 
                # 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 );
+               $obj = $dbw->getArray( 'cur', array( 'cur_is_redirect','cur_text' ), 
+                       array( 'cur_id' => $id ), $fname, 'FOR UPDATE' );
 
-               if ( 0 == $obj->cur_is_redirect ) { 
+               if ( !$obj || 0 == $obj->cur_is_redirect ) { 
                        # Not a redirect
                        return false; 
                }
@@ -1103,9 +1143,11 @@ class Title {
                }
 
                # Does the article have a history?
-               $row = wfGetArray( 'old', array( 'old_id' ), array( 
-                       'old_namespace' => $nt->getNamespace(),
-                       'old_title' => $nt->getDBkey() )
+               $row = $dbw->getArray( 'old', array( 'old_id' ), 
+                       array( 
+                               'old_namespace' => $nt->getNamespace(),
+                               'old_title' => $nt->getDBkey() 
+                       ), $fname, 'FOR UPDATE' 
                );
 
                # Return true if there was no history
@@ -1120,10 +1162,14 @@ class Title {
                        return false;
                }
                
+               $fname = "Title::createRedirect";
+               $dbw =& wfGetDB( DB_MASTER );
                $now = wfTimestampNow();
                $won = wfInvertTimestamp( $now );
+               $seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' );
 
-               wfInsertArray( 'cur', array(
+               $dbw->insertArray( 'cur', array(
+                       'cur_id' => $seqVal,
                        'cur_namespace' => $this->getNamespace(),
                        'cur_title' => $this->getDBkey(),
                        'cur_comment' => $comment,
@@ -1135,26 +1181,118 @@ class Title {
                        'cur_is_redirect' => 1,
                        'cur_is_new' => 1,
                        'cur_text' => "#REDIRECT [[" . $dest->getPrefixedText() . "]]\n" 
-               ));
-               $newid = wfInsertId();
+               ), $fname );
+               $newid = $dbw->insertId();
                $this->resetArticleID( $newid );
                
                # Link table
                if ( $dest->getArticleID() ) {
-                       wfInsertArray( 'links', array(
-                               'l_to' => $dest->getArticleID(),
-                               'l_from' => $newid
-                       ));
+                       $dbw->insertArray( 'links', 
+                               array(
+                                       'l_to' => $dest->getArticleID(),
+                                       'l_from' => $newid
+                               ), $fname 
+                       );
                } else {
-                       wfInsertArray( 'brokenlinks', array( 
-                               'bl_to' => $dest->getPrefixedDBkey(),
-                               'bl_from' => $newid
-                       ));
+                       $dbw->insertArray( 'brokenlinks', 
+                               array( 
+                                       'bl_to' => $dest->getPrefixedDBkey(),
+                                       'bl_from' => $newid
+                               ), $fname
+                       );
                }
 
                Article::onArticleCreate( $this );
                return true;
        }
        
+       # Get categories to wich belong this title and return an array of
+       # categories names.
+       function getParentCategories( )
+       {
+               global $wgLang,$wgUser;
+               
+               $titlekey = $this->getArticleId();
+               $cns = Namespace::getCategory();
+               $sk =& $wgUser->getSkin();
+               $parents = array();
+               $dbr =& wfGetDB( DB_SLAVE );
+               $cur = $dbr->tableName( 'cur' );
+               $categorylinks = $dbr->tableName( 'categorylinks' );
+
+               # get the parents categories of this title from the database
+               $sql = "SELECT DISTINCT cur_id FROM $cur,$categorylinks
+                       WHERE cl_from='$titlekey' AND cl_to=cur_title AND cur_namespace='$cns'
+                               ORDER BY cl_sortkey" ;
+               $res = $dbr->query ( $sql ) ;
+               
+               if($dbr->numRows($res) > 0) {
+                       while ( $x = $dbr->fetchObject ( $res ) ) $data[] = $x ;
+                       $dbr->freeResult ( $res ) ;
+               } else {
+                       $data = '';
+               }
+               return $data;
+       }
+       
+       # will get the parents and grand-parents
+       # TODO : not sure what's happening when a loop happen like:
+       #       Encyclopedia > Astronomy > Encyclopedia
+       function getAllParentCategories(&$stack)
+       {
+               global $wgUser,$wgLang;
+               $result = '';
+               
+               # getting parents
+               $parents = $this->getParentCategories( );
+
+               if($parents == '')
+               {
+                       # The current element has no more parent so we dump the stack
+                       # and make a clean line of categories
+                       $sk =& $wgUser->getSkin() ;
+
+                       foreach ( array_reverse($stack) as $child => $parent )
+                       {
+                               # make a link of that parent
+                               $result .= $sk->makeLink($wgLang->getNSText ( Namespace::getCategory() ).":".$parent,$parent);
+                               $result .= ' &gt; ';
+                               $lastchild = $child;
+                       }
+                       # append the last child.
+                       # TODO : We should have a last child unless there is an error in the
+                       # "categorylinks" table.
+                       if(isset($lastchild)) { $result .= $lastchild; }
+                       
+                       $result .= "<br/>\n";
+                       
+                       # now we can empty the stack
+                       $stack = array();
+                       
+               } else {
+                       # look at parents of current category
+                       foreach($parents as $parent)
+                       {
+                               # create a title object for the parent
+                               $tpar = Title::newFromID($parent->cur_id);
+                               # add it to the stack
+                               $stack[$this->getText()] = $tpar->getText();
+                               # grab its parents
+                               $result .= $tpar->getAllParentCategories($stack);
+                       }
+               }
+
+               if(isset($result)) { return $result; }
+               else { return ''; };
+       }
+       
+       # Returns an associative array for selecting this title from cur
+       function curCond() {
+               return array( 'cur_namespace' => $this->mNamespace, 'cur_title' => $this->mDbkeyform );
+       }
+
+       function oldCond() {
+               return array( 'old_namespace' => $this->mNamespace, 'old_title' => $this->mDbkeyform );
+       }
 }
 ?>