<?php
-# See title.doc
-
-/* private static */ $title_interwiki_cache = array();
-
-# Title class
-#
-# * Represents a title, which may contain an interwiki designation or namespace
-# * Can fetch various kinds of data from the database, albeit inefficiently.
-#
+/**
+ * $Id$
+ * See title.doc
+ *
+ * @package MediaWiki
+ */
+
+/** */
+require_once( 'normal/UtfNormal.php' );
+
+/**
+ *
+ */
+$wgTitleInterwikiCache = array();
+define ( 'GAID_FOR_UPDATE', 1 );
+
+/**
+ * Title class
+ * - Represents a title, which may contain an interwiki designation or namespace
+ * - Can fetch various kinds of data from the database, albeit inefficiently.
+ *
+ * @todo migrate comments to phpdoc format
+ * @package MediaWiki
+ */
class Title {
# All member variables should be considered private
# Please use the accessor functions
# 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
#----------------------------------------------------------------------------
- /* private */ function Title()
- {
+ /* private */ function Title() {
$this->mInterwiki = $this->mUrlform =
- $this->mTextform = $this->mDbkeyform = "";
+ $this->mTextform = $this->mDbkeyform = '';
$this->mArticleID = -1;
$this->mNamespace = 0;
$this->mRestrictionsLoaded = false;
$this->mRestrictions = array();
+ $this->mDefaultNamespace = 0;
}
# From a prefixed DB key
- /* static */ function newFromDBkey( $key )
- {
+ /* static */ function newFromDBkey( $key ) {
$t = new Title();
$t->mDbkeyform = $key;
if( $t->secureAndSplit() )
}
# From text, such as what you would find in a link
- /* static */ function newFromText( $text )
- {
- static $trans;
- $fname = "Title::newFromText";
+ /* static */ function newFromText( $text, $defaultNamespace = 0 ) {
+ $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 );
- }
- }
if( is_object( $text ) ) {
- wfDebugDieBacktrace( "Called with object instead of string." );
+ wfDebugDieBacktrace( 'Called with object instead of string.' );
}
- $text = strtr( $text, $trans );
-
+ global $wgInputEncoding;
+ $text = do_html_entity_decode( $text, ENT_COMPAT, $wgInputEncoding );
+
$text = wfMungeToUtf8( $text );
# $text = urldecode( $text );
$t = new Title();
- $t->mDbkeyform = str_replace( " ", "_", $text );
+ $t->mDbkeyform = str_replace( ' ', '_', $text );
+ $t->mDefaultNamespace = $defaultNamespace;
+
wfProfileOut( $fname );
+ if ( !is_object( $t ) ) {
+ var_dump( debug_backtrace() );
+ }
if( $t->secureAndSplit() ) {
return $t;
} else {
}
# From a URL-encoded title
- /* static */ function newFromURL( $url )
- {
+ /* static */ function newFromURL( $url ) {
global $wgLang, $wgServer;
$t = new Title();
- $s = urldecode( $url ); # This is technically wrong, as anything
- # we've gotten is already decoded by PHP.
- # Kept for backwards compatibility with
- # buggy URLs we had for a while...
- $s = $url;
-
- # For links that came from outside, check for alternate/legacy
- # character encoding.
- 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 );
+ # For compatibility with old buggy URLs. "+" is not valid in titles,
+ # but some URLs used it as a space replacement and they still come
+ # from some external search tools.
+ $s = str_replace( '+', ' ', $url );
+
+ $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;
}
# From a cur_id
# This is inefficiently implemented, the cur row is requested but not
# used for anything else
- /* static */ function newFromID( $id )
- {
- $fname = "Title::newFromID";
- $row = wfGetArray( "cur", array( "cur_namespace", "cur_title" ),
- array( "cur_id" => $id ), $fname );
+ /* static */ function newFromID( $id ) {
+ $fname = 'Title::newFromID';
+ $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 );
} else {
return $title;
}
- # From a namespace index and a DB key
- /* static */ function makeTitle( $ns, $title )
- {
+ # From a namespace index and a DB key.
+ # It's assumed that $ns and $title are *valid*, for instance when
+ # they came directly from the database or a special page name.
+ /* static */ function &makeTitle( $ns, $title ) {
+ $t =& new Title();
+ $t->mInterwiki = '';
+ $t->mFragment = '';
+ $t->mNamespace = $ns;
+ $t->mDbkeyform = $title;
+ $t->mArticleID = ( $ns >= 0 ) ? 0 : -1;
+ $t->mUrlform = wfUrlencode( $title );
+ $t->mTextform = str_replace( '_', ' ', $title );
+ return $t;
+ }
+
+ # From a namespace index and a DB key.
+ # These will be checked for validity, which is a bit slower
+ # than makeTitle() but safer for user-provided data.
+ /* static */ function makeTitleSafe( $ns, $title ) {
$t = new Title();
$t->mDbkeyform = Title::makeName( $ns, $title );
if( $t->secureAndSplit() ) {
} else {
return NULL;
}
+ }
+
+ /* static */ function newMainPage() {
+ return Title::newFromText( wfMsg( 'mainpage' ) );
}
- function newMainPage()
- {
- return Title::newFromText( wfMsg( "mainpage" ) );
+ # Get the title object for a redirect
+ # Returns NULL if the text is not a valid redirect
+ /* static */ function newFromRedirect( $text ) {
+ global $wgMwRedir;
+ $rt = NULL;
+ if ( $wgMwRedir->matchStart( $text ) ) {
+ if ( preg_match( '/\\[\\[([^\\]\\|]+)[\\]\\|]/', $text, $m ) ) {
+ # categories are escaped using : for example one can enter:
+ # #REDIRECT [[:Category:Music]]. Need to remove it.
+ if ( substr($m[1],0,1) == ':') {
+ # We don't want to keep the ':'
+ $m[1] = substr( $m[1], 1 );
+ }
+
+ $rt = Title::newFromText( $m[1] );
+ }
+ }
+ return $rt;
}
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
# 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; }
+ /* static */ function nameOf( $id ) {
+ $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;
}
# Get a regex character class describing the legal characters in a link
- /* static */ function legalChars()
- {
+ /* static */ function legalChars() {
# Missing characters:
# * []|# Needed for link syntax
# * % and + are corrupted by Apache when they appear in the path
- #
+ #
+ # % 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;
}
# Returns a stripped-down a title string ready for the search index
# Takes a namespace index and a text-form main part
- /* static */ function indexTitle( $ns, $title )
- {
+ /* static */ function indexTitle( $ns, $title ) {
global $wgDBminWordLen, $wgLang;
+ require_once( 'SearchEngine.php' );
- $lc = SearchEngine::legalSearchChars() . "&#;";
+ $lc = SearchEngine::legalSearchChars() . '&#;';
$t = $wgLang->stripForSearch( $title );
- $t = preg_replace( "/[^{$lc}]+/", " ", $t );
+ $t = preg_replace( "/[^{$lc}]+/", ' ', $t );
$t = strtolower( $t );
# Handle 's, s'
$t = preg_replace( "/([{$lc}]+)'s( |$)/", "\\1 \\1's ", $t );
$t = preg_replace( "/([{$lc}]+)s'( |$)/", "\\1s ", $t );
- $t = preg_replace( "/\\s+/", " ", $t );
+ $t = preg_replace( "/\\s+/", ' ', $t );
if ( $ns == Namespace::getImage() ) {
$t = preg_replace( "/ (png|gif|jpg|jpeg|ogg)$/", "", $t );
}
# Make a prefixed DB key from a DB key and a namespace index
- /* static */ function makeName( $ns, $title )
- {
+ /* static */ function makeName( $ns, $title ) {
global $wgLang;
$n = $wgLang->getNsText( $ns );
- if ( "" == $n ) { return $title; }
- else { return "{$n}:{$title}"; }
+ if ( '' == $n ) { return $title; }
+ else { return $n.':'.$title; }
}
# Arguably static
# Returns the URL associated with an interwiki prefix
# The URL contains $1, which is replaced by the title
- function getInterwikiLink( $key )
- {
- global $wgMemc, $wgDBname, $title_interwiki_cache;
- $k = "$wgDBname:interwiki:$key";
+ function getInterwikiLink( $key ) {
+ global $wgMemc, $wgDBname, $wgInterwikiExpiry, $wgTitleInterwikiCache;
+ $fname = 'Title::getInterwikiLink';
+ $k = $wgDBname.':interwiki:'.$key;
- if( array_key_exists( $k, $title_interwiki_cache ) )
- return $title_interwiki_cache[$k]->iw_url;
+ if( array_key_exists( $k, $wgTitleInterwikiCache ) )
+ return $wgTitleInterwikiCache[$k]->iw_url;
$s = $wgMemc->get( $k );
- if( $s ) {
- $title_interwiki_cache[$k] = $s;
+ # Ignore old keys with no iw_local
+ if( $s && isset( $s->iw_local ) ) {
+ $wgTitleInterwikiCache[$k] = $s;
return $s->iw_url;
}
- $dkey = wfStrencode( $key );
- $query = "SELECT iw_url 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_url = '';
+ $s->iw_local = 0;
}
- $wgMemc->set( $k, $s );
- $title_interwiki_cache[$k] = $s;
+ $wgMemc->set( $k, $s, $wgInterwikiExpiry );
+ $wgTitleInterwikiCache[$k] = $s;
return $s->iw_url;
}
-
+
+ function isLocal() {
+ global $wgTitleInterwikiCache, $wgDBname;
+
+ if ( $this->mInterwiki != '' ) {
+ # Make sure key is loaded into cache
+ $this->getInterwikiLink( $this->mInterwiki );
+ $k = $wgDBname.':interwiki:' . $this->mInterwiki;
+ return (bool)($wgTitleInterwikiCache[$k]->iw_local);
+ } else {
+ return true;
+ }
+ }
+
+ # 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;
+ }
+ $dbw =& wfGetDB( DB_MASTER );
+ if ( $timestamp == '' ) {
+ $timestamp = $dbw->timestamp();
+ }
+ $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 ) {
+ $dbw->query( $sql, 'Title::touchArray' );
+ }
+ }
+
#----------------------------------------------------------------------------
# Other stuff
#----------------------------------------------------------------------------
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()
- {
+ function getIndexTitle() {
return Title::indexTitle( $this->mNamespace, $this->mTextform );
}
# Get prefixed title with underscores
- function getPrefixedDBkey()
- {
+ function getPrefixedDBkey() {
$s = $this->prefix( $this->mDbkeyform );
- $s = str_replace( " ", "_", $s );
+ $s = str_replace( ' ', '_', $s );
return $s;
}
# Get prefixed title with spaces
# This is the form usually used for display
- function getPrefixedText()
- {
+ function getPrefixedText() {
if ( empty( $this->mPrefixedText ) ) {
$s = $this->prefix( $this->mTextform );
- $s = str_replace( "_", " ", $s );
+ $s = str_replace( '_', ' ', $s );
$this->mPrefixedText = $s;
}
return $this->mPrefixedText;
}
+
+ # As getPrefixedText(), plus fragment.
+ function getFullText() {
+ $text = $this->getPrefixedText();
+ if( '' != $this->mFragment ) {
+ $text .= '#' . $this->mFragment;
+ }
+ return $text;
+ }
# Get a URL-encoded title (not an actual URL) including interwiki
- function getPrefixedURL()
- {
+ function getPrefixedURL() {
$s = $this->prefix( $this->mDbkeyform );
- $s = str_replace( " ", "_", $s );
+ $s = str_replace( ' ', '_', $s );
$s = wfUrlencode ( $s ) ;
# Cleaning up URL to make it look nice -- is this safe?
- $s = preg_replace( "/%3[Aa]/", ":", $s );
- $s = preg_replace( "/%2[Ff]/", "/", $s );
- $s = str_replace( "%28", "(", $s );
- $s = str_replace( "%29", ")", $s );
+ $s = preg_replace( '/%3[Aa]/', ':', $s );
+ $s = preg_replace( '/%2[Ff]/', '/', $s );
+ $s = str_replace( '%28', '(', $s );
+ $s = str_replace( '%29', ')', $s );
return $s;
}
# Get a real URL referring to this title, with interwiki link and fragment
- function getFullURL( $query = "" )
- {
+ function getFullURL( $query = '' ) {
global $wgLang, $wgArticlePath, $wgServer, $wgScript;
- if ( "" == $this->mInterwiki ) {
+ if ( '' == $this->mInterwiki ) {
$p = $wgArticlePath;
return $wgServer . $this->getLocalUrl( $query );
+ } else {
+ $baseUrl = $this->getInterwikiLink( $this->mInterwiki );
+ $namespace = $wgLang->getNsText( $this->mNamespace );
+ if ( '' != $namespace ) {
+ # Can this actually happen? Interwikis shouldn't be parsed.
+ $namepace .= ':';
+ }
+ $url = str_replace( '$1', $namespace . $this->mUrlform, $baseUrl );
+ if ( '' != $this->mFragment ) {
+ $url .= '#' . $this->mFragment;
+ }
+ return $url;
}
-
- $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 .= "#" . wfUrlencode( $this->mFragment );
- }
- return $u;
}
# Get a URL with an optional query string, no fragment
# query except a title
function getURL() {
- die( "Call to obsolete obsolete function Title::getURL()" );
+ die( 'Call to obsolete obsolete function Title::getURL()' );
}
- function getLocalURL( $query = "" )
- {
+ function getLocalURL( $query = '' ) {
global $wgLang, $wgArticlePath, $wgScript;
if ( $this->isExternal() ) {
}
$dbkey = wfUrlencode( $this->getPrefixedDBkey() );
- if ( $query == "" ) {
- $url = str_replace( "$1", $dbkey, $wgArticlePath );
+ if ( $query == '' ) {
+ $url = str_replace( '$1', $dbkey, $wgArticlePath );
} else {
- if ( $query == "-" ) {
- $query = "";
+ if ( $query == '-' ) {
+ $query = '';
}
- if ( $wgScript != "" ) {
+ if ( $wgScript != '' ) {
$url = "{$wgScript}?title={$dbkey}&{$query}";
} else {
# Top level wiki
return $url;
}
- function escapeLocalURL( $query = "" ) {
- return wfEscapeHTML( $this->getLocalURL( $query ) );
+ function escapeLocalURL( $query = '' ) {
+ return htmlspecialchars( $this->getLocalURL( $query ) );
}
- function escapeFullURL( $query = "" ) {
- return wfEscapeHTML( $this->getFullURL( $query ) );
+ function escapeFullURL( $query = '' ) {
+ return htmlspecialchars( $this->getFullURL( $query ) );
}
- function getInternalURL( $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;
}
# Get the edit URL, or a null string if it is an interwiki link
- function getEditURL()
- {
+ function getEditURL() {
global $wgServer, $wgScript;
- if ( "" != $this->mInterwiki ) { return ""; }
- $s = $this->getLocalURL( "action=edit" );
+ if ( '' != $this->mInterwiki ) { return ''; }
+ $s = $this->getLocalURL( 'action=edit' );
return $s;
}
# Get HTML-escaped displayable text
# For the title field in <a> tags
- function getEscapedText()
- {
- return wfEscapeHTML( $this->getPrefixedText() );
+ function getEscapedText() {
+ return htmlspecialchars( $this->getPrefixedText() );
}
# Is the title interwiki?
- function isExternal() { return ( "" != $this->mInterwiki ); }
+ function isExternal() { return ( '' != $this->mInterwiki ); }
# Does the title correspond to a protected article?
- function isProtected()
- {
+ function isProtected() {
if ( -1 == $this->mNamespace ) { return true; }
$a = $this->getRestrictions();
- if ( in_array( "sysop", $a ) ) { return true; }
+ if ( in_array( 'sysop', $a ) ) { return true; }
return false;
}
# LogPage.php? This used to be used for suppressing diff links in recent
# changes, but now that's done by setting a flag in the recentchanges
# table. Hence, this probably is no longer used.
- function isLog()
- {
+ function isLog() {
if ( $this->mNamespace != Namespace::getWikipedia() ) {
return false;
}
- if ( ( 0 == strcmp( wfMsg( "uploadlogpage" ), $this->mDbkeyform ) ) ||
- ( 0 == strcmp( wfMsg( "dellogpage" ), $this->mDbkeyform ) ) ) {
+ if ( ( 0 == strcmp( wfMsg( 'uploadlogpage' ), $this->mDbkeyform ) ) ||
+ ( 0 == strcmp( wfMsg( 'dellogpage' ), $this->mDbkeyform ) ) ) {
return true;
}
return false;
}
# Is $wgUser is watching this page?
- function userIsWatching()
- {
+ function userIsWatching() {
global $wgUser;
if ( -1 == $this->mNamespace ) { return false; }
}
# Can $wgUser edit this page?
- function userCanEdit()
- {
+ 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; }
-
+ 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
+ if( Namespace::getUser() == $this->mNamespace
+ and preg_match("/\\.(css|js)$/", $this->mTextform )
+ and !$wgUser->isSysop()
+ and !preg_match('/^'.preg_quote($wgUser->getName(), '/').'/', $this->mTextform) )
+ { return false; }
$ur = $wgUser->getRights();
foreach ( $this->getRestrictions() as $r ) {
- if ( "" != $r && ( ! in_array( $r, $ur ) ) ) {
+ if ( '' != $r && ( ! in_array( $r, $ur ) ) ) {
return false;
}
}
return true;
}
+
+ function userCanRead() {
+ global $wgUser;
+ global $wgWhitelistRead;
+
+ if( 0 != $wgUser->getID() ) return true;
+ if( !is_array( $wgWhitelistRead ) ) return true;
+
+ $name = $this->getPrefixedText();
+ if( in_array( $name, $wgWhitelistRead ) ) return true;
+
+ # Compatibility with old settings
+ if( $this->getNamespace() == NS_MAIN ) {
+ if( in_array( ':' . $name, $wgWhitelistRead ) ) return true;
+ }
+ return false;
+ }
+
+ function isCssJsSubpage() {
+ return ( Namespace::getUser() == $this->mNamespace and preg_match("/\\.(css|js)$/", $this->mTextform ) );
+ }
+ function isCssSubpage() {
+ return ( Namespace::getUser() == $this->mNamespace and preg_match("/\\.css$/", $this->mTextform ) );
+ }
+ function isJsSubpage() {
+ return ( Namespace::getUser() == $this->mNamespace and preg_match("/\\.js$/", $this->mTextform ) );
+ }
+ function userCanEditCssJsSubpage() {
+ # protect css/js subpages of user pages
+ # XXX: this might be better using restrictions
+ global $wgUser;
+ return ( $wgUser->isSysop() or preg_match('/^'.preg_quote($wgUser->getName()).'/', $this->mTextform) );
+ }
# Accessor/initialisation for mRestrictions
- function getRestrictions()
- {
+ function getRestrictions() {
$id = $this->getArticleID();
if ( 0 == $id ) { return array(); }
if ( ! $this->mRestrictionsLoaded ) {
- $res = wfGetSQL( "cur", "cur_restrictions", "cur_id=$id" );
- $this->mRestrictions = explode( ",", trim( $res ) );
+ $dbr =& wfGetDB( DB_SLAVE );
+ $res = $dbr->getField( 'cur', 'cur_restrictions', 'cur_id='.$id );
+ $this->mRestrictions = explode( ',', trim( $res ) );
$this->mRestrictionsLoaded = true;
}
return $this->mRestrictions;
}
# 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
- # Used very heavily, e.g. in Parser::replaceInternalLinks()
- function getArticleID()
- {
+ # $flags is a bit field, may be GAID_FOR_UPDATE to select for update
+ function getArticleID( $flags = 0 ) {
global $wgLinkCache;
-
- if ( -1 != $this->mArticleID ) { return $this->mArticleID; }
- $this->mArticleID = $wgLinkCache->addLinkObj( $this );
+
+ if ( $flags & GAID_FOR_UPDATE ) {
+ $oldUpdate = $wgLinkCache->forUpdate( true );
+ $this->mArticleID = $wgLinkCache->addLinkObj( $this );
+ $wgLinkCache->forUpdate( $oldUpdate );
+ } else {
+ if ( -1 == $this->mArticleID ) {
+ $this->mArticleID = $wgLinkCache->addLinkObj( $this );
+ }
+ }
return $this->mArticleID;
}
# This clears some fields in this object, and clears any associated keys in the
# "bad links" section of $wgLinkCache. This is called from Article::insertNewArticle()
# to allow loading of the new cur_id. It's also called from Article::doDeleteArticle()
- function resetArticleID( $newid )
- {
+ function resetArticleID( $newid ) {
global $wgLinkCache;
$wgLinkCache->clearBadLink( $this->getPrefixedDBkey() );
# 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' => $dbw->timestamp()
+ ), 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
- /* private */ function prefix( $name )
- {
+ /* private */ function prefix( $name ) {
global $wgLang;
- $p = "";
- if ( "" != $this->mInterwiki ) {
- $p = $this->mInterwiki . ":";
+ $p = '';
+ if ( '' != $this->mInterwiki ) {
+ $p = $this->mInterwiki . ':';
}
if ( 0 != $this->mNamespace ) {
- $p .= $wgLang->getNsText( $this->mNamespace ) . ":";
+ $p .= $wgLang->getNsText( $this->mNamespace ) . ':';
}
return $p . $name;
}
# 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.
#
/* private */ function secureAndSplit()
{
- global $wgLang, $wgLocalInterwiki;
- $fname = "Title::secureAndSplit";
+ global $wgLang, $wgLocalInterwiki, $wgCapitalLinks;
+ $fname = 'Title::secureAndSplit';
wfProfileIn( $fname );
static $imgpre = false;
# Initialisation
if ( $imgpre === false ) {
- $imgpre = ":" . $wgLang->getNsText( Namespace::getImage() ) . ":";
- $rxTc = "/[^" . Title::legalChars() . "]/";
+ $imgpre = ':' . $wgLang->getNsText( Namespace::getImage() ) . ':';
+ # % is needed as well
+ $rxTc = '/[^' . Title::legalChars() . ']/';
}
-
- $this->mInterwiki = $this->mFragment = "";
- $this->mNamespace = 0;
+ $this->mInterwiki = $this->mFragment = '';
+ $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( "/[\\s_]+/", '_', $this->mDbkeyform );
+ $t = preg_replace( '/^_*(.*?)_*$/', '$1', $t );
+
+ if ( '' == $t ) {
+ wfProfileOut( $fname );
+ return false;
}
- if ( "" == $t ) {
+
+ global $wgUseLatin1;
+ if( !$wgUseLatin1 && false !== strpos( $t, UTF8_REPLACEMENT ) ) {
+ # Contained illegal UTF-8 sequences or forbidden Unicode chars.
wfProfileOut( $fname );
return false;
}
$t = substr( $t, 1 );
}
- # Redundant initial colon
- if ( ":" == $t{0} ) {
+ # 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( "/^(.+?)_*:_*(.*)$/", $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;
# Redundant interwiki prefix to the local wiki
if ( 0 == strcmp( $this->mInterwiki, $wgLocalInterwiki ) ) {
- $this->mInterwiki = "";
+ $this->mInterwiki = '';
}
# We already know that some pages won't be in the database!
#
- if ( "" != $this->mInterwiki || -1 == $this->mNamespace ) {
+ if ( '' != $this->mInterwiki || -1 == $this->mNamespace ) {
$this->mArticleID = 0;
}
- $f = strstr( $r, "#" );
+ $f = strstr( $r, '#' );
if ( false !== $f ) {
$this->mFragment = substr( $f, 1 );
$r = substr( $r, 0, strlen( $r ) - strlen( $f ) );
# Reject illegal characters.
#
if( preg_match( $rxTc, $r ) ) {
+ wfProfileOut( $fname );
return false;
}
- # "." and ".." conflict with the directories of those names
- if ( $r === "." || $r === ".." ) {
+ # "." and ".." conflict with the directories of those namesa
+ if ( strpos( $r, '.' ) !== false &&
+ ( $r === '.' || $r === '..' ||
+ strpos( $r, './' ) === 0 ||
+ strpos( $r, '../' ) === 0 ||
+ strpos( $r, '/./' ) !== false ||
+ strpos( $r, '/../' ) !== false ) )
+ {
+ wfProfileOut( $fname );
return false;
}
# Initial capital letter
- if( $this->mInterwiki == "") $t = $wgLang->ucfirst( $r );
-
+ if( $wgCapitalLinks && $this->mInterwiki == '') {
+ $t = $wgLang->ucfirst( $r );
+ } else {
+ $t = $r;
+ }
+
# Fill fields
$this->mDbkeyform = $t;
$this->mUrlform = wfUrlencode( $t );
- $this->mTextform = str_replace( "_", " ", $t );
+ $this->mTextform = str_replace( '_', ' ', $t );
wfProfileOut( $fname );
return true;
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
+ # $options may be FOR UPDATE
+ function getLinksTo( $options = '' ) {
+ global $wgLinkCache;
+ $id = $this->getArticleID();
+
+ 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 ( $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;
+ }
+ }
+ }
+ $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( $options = '' ) {
+ global $wgLinkCache;
+
+ 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 ( $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;
+ }
+ }
+ $db->freeResult( $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 ) {
+ if( !$this or !$nt ) {
+ return 'badtitletext';
+ }
+
+ $fname = 'Title::move';
+ $oldid = $this->getArticleID();
+ $newid = $nt->getArticleID();
+
+ 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 );
+ }
+
+ # Fixing category links (those without piped 'alternate' names) to be sorted under the new title
+
+ $dbw =& wfGetDB( DB_MASTER );
+ $sql = "UPDATE categorylinks SET cl_sortkey=" . $dbw->addQuotes( $nt->getPrefixedText() ) .
+ " WHERE cl_from=" . $dbw->addQuotes( $this->getArticleID() ) .
+ " AND cl_sortkey=" . $dbw->addQuotes( $this->getPrefixedText() );
+ $dbw->query( $sql, 'SpecialMovepage::doSubmit' );
+
+ # 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();
+ $dbw =& wfGetDB( DB_MASTER );
+ $links = $dbw->tableName( 'links' );
+
+ # Change the name of the target page:
+ $dbw->updateArray( 'cur',
+ /* SET */ array(
+ 'cur_touched' => $dbw->timestamp($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";
+ $dbw->updateArray( 'cur',
+ /* SET */ array(
+ 'cur_touched' => $dbw->timestamp($now),
+ 'cur_timestamp' => $dbw->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.
+ $dbw->updateArray(
+ /* table */ 'old',
+ /* SET */ array(
+ 'old_namespace' => $nt->getNamespace(),
+ 'old_title' => $nt->getDBkey(),
+ ),
+ /* WHERE */ array(
+ 'old_namespace' => $this->getNamespace(),
+ 'old_title' => $this->getDBkey(),
+ ),
+ $fname
+ );
+
+ RecentChange::notifyMoveOverRedirect( $now, $this, $nt, $wgUser, $comment );
+
+ # Swap links
+
+ # Load titles and IDs
+ $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";
+ $dbw->query( $sql, $fname );
+
+ # Reinsert
+ if ( count( $linksToOld ) || count( $linksToNew )) {
+ $sql = "INSERT INTO $links (l_from,l_to) VALUES ";
+ $first = true;
+
+ # Insert links to old title
+ foreach ( $linksToOld as $linkTitle ) {
+ if ( $first ) {
+ $first = false;
+ } else {
+ $sql .= ',';
+ }
+ $id = $linkTitle->getArticleID();
+ $sql .= "($id,$newid)";
+ }
+
+ # Insert links to new title
+ foreach ( $linksToNew as $linkTitle ) {
+ if ( $first ) {
+ $first = false;
+ } else {
+ $sql .= ',';
+ }
+ $id = $linkTitle->getArticleID();
+ $sql .= "($id, $oldid)";
+ }
+
+ $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...
+ $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() );
+ $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() );
+
+ $newid = $nt->getArticleID();
+ $oldid = $this->getArticleID();
+ $dbw =& wfGetDB( DB_MASTER );
+ $now = $dbw->timestamp();
+ $won = wfInvertTimestamp( wfTimestamp(TS_MW,$now) );
+
+ # Rename cur entry
+ $dbw->updateArray( '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 redirect
+ $dbw->insertArray( 'cur', array(
+ 'cur_id' => $dbw->nextSequenceValue('cur_cur_id_seq'),
+ '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" ), $fname
+ );
+ $newid = $dbw->insertId();
+ $wgLinkCache->clearLink( $this->getPrefixedDBkey() );
+
+ # Rename old entries
+ $dbw->updateArray(
+ /* table */ 'old',
+ /* SET */ array(
+ 'old_namespace' => $nt->getNamespace(),
+ 'old_title' => $nt->getDBkey()
+ ),
+ /* WHERE */ array(
+ 'old_namespace' => $this->getNamespace(),
+ 'old_title' => $this->getDBkey()
+ ), $fname
+ );
+
+ # Record in RC
+ RecentChange::notifyMoveToNew( $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
+ $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
+ $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.
+ $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
+ # 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();
+ $obj = $dbw->getArray( 'cur', array( 'cur_is_redirect','cur_text' ),
+ array( 'cur_id' => $id ), $fname, 'FOR UPDATE' );
+
+ if ( !$obj || 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( !is_object( $redirTitle ) ||
+ $redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() ) {
+ return false;
+ }
+ }
+
+ # Does the article have a history?
+ $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
+ 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;
+ }
+
+ $fname = 'Title::createRedirect';
+ $dbw =& wfGetDB( DB_MASTER );
+ $now = wfTimestampNow();
+ $won = wfInvertTimestamp( $now );
+ $seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' );
+
+ $dbw->insertArray( 'cur', array(
+ 'cur_id' => $seqVal,
+ '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"
+ ), $fname );
+ $newid = $dbw->insertId();
+ $this->resetArticleID( $newid );
+
+ # Link table
+ if ( $dest->getArticleID() ) {
+ $dbw->insertArray( 'links',
+ array(
+ 'l_to' => $dest->getArticleID(),
+ 'l_from' => $newid
+ ), $fname
+ );
+ } else {
+ $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.
+ # Return an array of parents in the form:
+ # $parent => $currentarticle
+ function getParentCategories() {
+ global $wgLang,$wgUser;
+
+ $titlekey = $this->getArticleId();
+ $sk =& $wgUser->getSkin();
+ $parents = array();
+ $dbr =& wfGetDB( DB_SLAVE );
+ $cur = $dbr->tableName( 'cur' );
+ $categorylinks = $dbr->tableName( 'categorylinks' );
+
+ # NEW SQL
+ $sql = "SELECT * FROM categorylinks"
+ ." WHERE cl_from='$titlekey'"
+ ." AND cl_from <> '0'"
+ ." ORDER BY cl_sortkey";
+
+ $res = $dbr->query ( $sql ) ;
+
+ if($dbr->numRows($res) > 0) {
+ while ( $x = $dbr->fetchObject ( $res ) )
+ //$data[] = Title::newFromText($wgLang->getNSText ( NS_CATEGORY ).':'.$x->cl_to);
+ $data[$wgLang->getNSText ( NS_CATEGORY ).':'.$x->cl_to] = $this->getFullText();
+ $dbr->freeResult ( $res ) ;
+ } else {
+ $data = '';
+ }
+ return $data;
+ }
+
+ # Go through all parents
+ function getCategorieBrowser() {
+ $parents = $this->getParentCategories();
+
+ if($parents != '') {
+ foreach($parents as $parent => $current)
+ {
+ $nt = Title::newFromText($parent);
+ $stack[$parent] = $nt->getCategorieBrowser();
+ }
+ return $stack;
+ } else {
+ return array();
+ }
+ }
+
+
+ # 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 );
+ }
}
?>