2004-02-18 02:15:00 +00:00
< ? php
2004-09-02 23:28:24 +00:00
/**
2005-04-12 02:07:16 +00:00
* See title . txt
2011-06-28 18:21:59 +00:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
* http :// www . gnu . org / copyleft / gpl . html
*
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
* @ file
2004-09-02 23:28:24 +00:00
*/
2003-04-14 23:10:40 +00:00
2004-09-02 23:28:24 +00:00
/**
2008-08-11 04:39:00 +00:00
* Represents a title within MediaWiki .
* Optionally may contain an interwiki designation or namespace .
* @ note This class can fetch various kinds of data from the database ;
* however , it does so inefficiently .
2010-03-15 23:22:50 +00:00
*
* @ internal documentation reviewed 15 Mar 2010
2004-09-03 23:00:01 +00:00
*/
2012-01-30 12:54:17 +00:00
class Title {
2008-08-11 04:39:00 +00:00
/** @name Static cache variables */
2010-04-24 06:22:11 +00:00
// @{
static private $titleCache = array ();
// @}
2008-04-14 07:45:50 +00:00
2008-08-16 10:13:35 +00:00
/**
* Title :: newFromText maintains a cache to avoid expensive re - normalization of
* commonly used titles . On a batch operation this can become a memory leak
* if not bounded . After hitting this many titles reset the cache .
*/
const CACHE_MAX = 1000 ;
2010-09-30 19:13:25 +00:00
/**
2011-01-23 01:59:24 +00:00
* Used to be GAID_FOR_UPDATE define . Used with getArticleID () and friends
2012-01-03 21:44:41 +00:00
* to use the master DB
2010-09-30 19:13:25 +00:00
*/
const GAID_FOR_UPDATE = 1 ;
2004-09-30 05:21:20 +00:00
/**
2008-08-11 04:39:00 +00:00
* @ name Private member variables
* Please use the accessor functions instead .
2006-06-10 18:28:50 +00:00
* @ private
2004-09-30 05:21:20 +00:00
*/
2010-04-24 06:22:11 +00:00
// @{
var $mTextform = '' ; // /< Text form (spaces not underscores) of the main part
var $mUrlform = '' ; // /< URL-encoded form of the main part
var $mDbkeyform = '' ; // /< Main part with underscores
var $mUserCaseDBKey ; // /< DB key with the initial letter in the case specified by the user
var $mNamespace = NS_MAIN ; // /< Namespace index, i.e. one of the NS_xxxx constants
var $mInterwiki = '' ; // /< Interwiki prefix (or null string)
var $mFragment ; // /< Title fragment (i.e. the bit after the #)
var $mArticleID = - 1 ; // /< Article ID, fetched from the link cache on demand
var $mLatestID = false ; // /< ID of most recent revision
2011-12-27 15:29:44 +00:00
private $mEstimateRevisions ; // /< Estimated number of revisions; null of not loaded
2010-04-24 06:22:11 +00:00
var $mRestrictions = array (); // /< Array of groups allowed to edit this article
2012-01-03 21:44:41 +00:00
var $mOldRestrictions = false ;
2009-12-14 20:37:51 +00:00
var $mCascadeRestriction ; ///< Cascade restrictions on this page to included templates and images?
2010-04-23 18:37:33 +00:00
var $mCascadingRestrictions ; // Caching the results of getCascadeProtectionSources
2009-12-14 20:37:51 +00:00
var $mRestrictionsExpiry = array (); ///< When do the restrictions on this page expire?
var $mHasCascadingRestrictions ; ///< Are cascading restrictions in effect on this page?
var $mCascadeSources ; ///< Where are the cascading restrictions coming from on this page?
2008-08-11 04:39:00 +00:00
var $mRestrictionsLoaded = false ; ///< Boolean for initialisation on demand
2009-12-14 20:37:51 +00:00
var $mPrefixedText ; ///< Text form including namespace/interwiki, initialised on demand
2011-01-06 21:26:20 +00:00
var $mTitleProtection ; ///< Cached value for getTitleProtection (create protection)
2008-07-25 20:16:24 +00:00
# Don't change the following default, NS_MAIN is hardcoded in several
# places. See bug 696.
2010-04-24 06:22:11 +00:00
var $mDefaultNamespace = NS_MAIN ; // /< Namespace index when there is no namespace
2011-02-12 04:06:22 +00:00
# Zero except in {{transclusion}} tags
2010-04-24 06:22:11 +00:00
var $mWatched = null ; // /< Is $wgUser watching this page? null if unfilled, accessed through userIsWatching()
var $mLength = - 1 ; // /< The page length, 0 for special pages
var $mRedirect = null ; // /< Is the article at this title a redirect?
var $mNotificationTimestamp = array (); // /< Associative array of user ID -> timestamp/false
var $mBacklinkCache = null ; // /< Cache of links to this title
// @}
2005-07-01 10:44:48 +00:00
2003-04-14 23:10:40 +00:00
2004-09-30 05:21:20 +00:00
/**
* Constructor
*/
2011-04-19 10:40:32 +00:00
/*protected*/ function __construct () { }
2003-04-14 23:10:40 +00:00
2004-09-30 05:21:20 +00:00
/**
* Create a new Title from a prefixed DB key
2010-03-23 21:40:02 +00:00
*
2011-02-02 11:00:09 +00:00
* @ param $key String the database key , which has underscores
2004-09-30 05:21:20 +00:00
* instead of spaces , possibly including namespace and
* interwiki prefixes
2011-02-02 11:00:09 +00:00
* @ return Title , or NULL on an error
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public static function newFromDBkey ( $key ) {
2003-04-14 23:10:40 +00:00
$t = new Title ();
$t -> mDbkeyform = $key ;
2010-07-25 15:53:22 +00:00
if ( $t -> secureAndSplit () ) {
2003-10-01 10:26:26 +00:00
return $t ;
2010-07-25 15:53:22 +00:00
} else {
2009-12-11 21:07:27 +00:00
return null ;
2010-07-25 15:53:22 +00:00
}
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
2008-10-10 15:37:44 +00:00
* Create a new Title from text , such as what one would find in a link . De -
* codes any HTML entities in the text .
2004-09-30 05:21:20 +00:00
*
2011-02-02 11:00:09 +00:00
* @ param $text String the link text ; spaces , prefixes , and an
2008-10-10 15:37:44 +00:00
* initial ':' indicating the main namespace are accepted .
2011-02-02 11:00:09 +00:00
* @ param $defaultNamespace Int the namespace to use if none is speci -
2008-10-10 15:37:51 +00:00
* fied by a prefix . If you want to force a specific namespace even if
* $text might begin with a namespace prefix , use makeTitle () or
* makeTitleSafe () .
2011-02-02 11:00:09 +00:00
* @ return Title , or null on an error .
2004-09-30 05:21:20 +00:00
*/
2006-07-10 15:08:51 +00:00
public static function newFromText ( $text , $defaultNamespace = NS_MAIN ) {
2010-04-24 06:22:11 +00:00
if ( is_object ( $text ) ) {
2006-06-07 06:40:24 +00:00
throw new MWException ( 'Title::newFromText given an object' );
2005-02-21 11:28:07 +00:00
}
2005-07-01 10:44:48 +00:00
2004-11-26 11:47:09 +00:00
/**
* Wiki pages often contain multiple links to the same page .
* Title normalization and parsing can become expensive on
* pages with many links , so we can save a little time by
* caching them .
*
* In theory these are value objects and won ' t get changed ...
*/
2010-04-24 06:22:11 +00:00
if ( $defaultNamespace == NS_MAIN && isset ( Title :: $titleCache [ $text ] ) ) {
2006-06-08 13:08:22 +00:00
return Title :: $titleCache [ $text ];
2004-11-26 11:47:09 +00:00
}
2004-04-09 07:02:29 +00:00
2011-02-02 11:00:09 +00:00
# Convert things like é ā or 〗 into normalized (bug 14952) text
2010-03-28 03:10:10 +00:00
$filteredText = Sanitizer :: decodeCharReferencesAndNormalize ( $text );
2003-04-14 23:10:40 +00:00
2006-07-11 14:11:23 +00:00
$t = new Title ();
2004-11-26 11:47:09 +00:00
$t -> mDbkeyform = str_replace ( ' ' , '_' , $filteredText );
2004-04-09 07:02:29 +00:00
$t -> mDefaultNamespace = $defaultNamespace ;
2006-01-10 17:56:59 +00:00
static $cachedcount = 0 ;
2010-04-24 06:22:11 +00:00
if ( $t -> secureAndSplit () ) {
if ( $defaultNamespace == NS_MAIN ) {
if ( $cachedcount >= self :: CACHE_MAX ) {
2005-01-15 07:08:37 +00:00
# Avoid memory leaks on mass operations...
2006-06-08 13:08:22 +00:00
Title :: $titleCache = array ();
2010-04-24 06:22:11 +00:00
$cachedcount = 0 ;
2005-01-15 07:08:37 +00:00
}
2006-01-10 17:56:59 +00:00
$cachedcount ++ ;
2006-06-08 13:08:22 +00:00
Title :: $titleCache [ $text ] =& $t ;
2004-11-26 11:47:09 +00:00
}
2003-10-01 10:26:26 +00:00
return $t ;
} else {
2009-12-11 21:07:27 +00:00
$ret = null ;
2005-08-02 13:35:19 +00:00
return $ret ;
2003-10-01 10:26:26 +00:00
}
2003-04-14 23:10:40 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2009-06-02 04:05:47 +00:00
* THIS IS NOT THE FUNCTION YOU WANT . Use Title :: newFromText () .
*
* Example of wrong and broken code :
* $title = Title :: newFromURL ( $wgRequest -> getVal ( 'title' ) );
*
* Example of right code :
* $title = Title :: newFromText ( $wgRequest -> getVal ( 'title' ) );
*
2004-09-30 05:21:20 +00:00
* Create a new Title from URL - encoded text . Ensures that
* the given title ' s length does not exceed the maximum .
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $url String the title , as might be taken from a URL
* @ return Title the new object , or NULL on an error
2004-09-30 05:21:20 +00:00
*/
2007-03-27 21:43:21 +00:00
public static function newFromURL ( $url ) {
2006-01-05 05:27:16 +00:00
global $wgLegalTitleChars ;
2003-04-14 23:10:40 +00:00
$t = new Title ();
2005-07-01 10:44:48 +00:00
2006-01-05 05:27:16 +00:00
# For compatibility with old buggy URLs. "+" is usually not valid in titles,
2004-05-30 06:43:26 +00:00
# but some URLs used it as a space replacement and they still come
# from some external search tools.
2006-01-05 05:27:16 +00:00
if ( strpos ( $wgLegalTitleChars , '+' ) === false ) {
$url = str_replace ( '+' , ' ' , $url );
}
2005-07-01 10:44:48 +00:00
2006-01-05 05:27:16 +00:00
$t -> mDbkeyform = str_replace ( ' ' , '_' , $url );
2010-04-24 06:22:11 +00:00
if ( $t -> secureAndSplit () ) {
2003-10-01 10:26:26 +00:00
return $t ;
} else {
2009-12-11 21:07:27 +00:00
return null ;
2003-10-01 10:26:26 +00:00
}
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
* Create a new Title from an article ID
2005-05-04 00:33:08 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $id Int the page_id corresponding to the Title to create
2012-01-03 21:44:41 +00:00
* @ param $flags Int use Title :: GAID_FOR_UPDATE to use master
2011-01-05 23:08:13 +00:00
* @ return Title the new object , or NULL on an error
2004-09-30 05:21:20 +00:00
*/
2008-05-10 23:31:52 +00:00
public static function newFromID ( $id , $flags = 0 ) {
2012-01-03 21:44:41 +00:00
$db = ( $flags & self :: GAID_FOR_UPDATE ) ? wfGetDB ( DB_MASTER ) : wfGetDB ( DB_SLAVE );
2009-03-27 10:22:30 +00:00
$row = $db -> selectRow ( 'page' , '*' , array ( 'page_id' => $id ), __METHOD__ );
2010-04-24 06:22:11 +00:00
if ( $row !== false ) {
2009-03-27 10:22:30 +00:00
$title = Title :: newFromRow ( $row );
2004-01-17 05:49:39 +00:00
} else {
2009-12-11 21:07:27 +00:00
$title = null ;
2004-01-17 05:49:39 +00:00
}
return $title ;
}
2005-07-01 10:44:48 +00:00
2006-06-18 12:42:16 +00:00
/**
2008-04-14 07:45:50 +00:00
* Make an array of titles from an array of IDs
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $ids Array of Int Array of IDs
* @ return Array of Titles
2006-06-18 12:42:16 +00:00
*/
2007-04-16 15:24:04 +00:00
public static function newFromIDs ( $ids ) {
2008-01-17 12:33:35 +00:00
if ( ! count ( $ids ) ) {
return array ();
}
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2011-02-12 04:06:22 +00:00
2012-01-03 21:44:41 +00:00
$res = $dbr -> select (
'page' ,
array (
'page_namespace' , 'page_title' , 'page_id' ,
'page_len' , 'page_is_redirect' , 'page_latest' ,
),
array ( 'page_id' => $ids ),
__METHOD__
);
2006-06-18 12:42:16 +00:00
$titles = array ();
2010-04-24 06:22:11 +00:00
foreach ( $res as $row ) {
2010-07-18 09:36:56 +00:00
$titles [] = Title :: newFromRow ( $row );
2006-06-18 12:42:16 +00:00
}
return $titles ;
}
2008-04-14 07:45:50 +00:00
2008-04-08 20:34:09 +00:00
/**
* Make a Title object from a DB row
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $row Object database row ( needs at least page_title , page_namespace )
* @ return Title corresponding Title
2008-04-08 20:34:09 +00:00
*/
public static function newFromRow ( $row ) {
$t = self :: makeTitle ( $row -> page_namespace , $row -> page_title );
2011-07-01 23:33:34 +00:00
$t -> loadFromRow ( $row );
2008-04-08 20:34:09 +00:00
return $t ;
}
2006-06-18 12:42:16 +00:00
2011-07-01 23:33:34 +00:00
/**
* Load Title object fields from a DB row .
* If false is given , the title will be treated as non - existing .
*
2012-02-09 18:01:10 +00:00
* @ param $row Object | bool database row
2011-07-01 23:33:34 +00:00
*/
2012-01-03 21:44:41 +00:00
public function loadFromRow ( $row ) {
2011-07-01 23:33:34 +00:00
if ( $row ) { // page found
2012-01-03 21:44:41 +00:00
if ( isset ( $row -> page_id ) )
2012-01-03 19:28:03 +00:00
$this -> mArticleID = ( int ) $row -> page_id ;
2012-01-03 21:44:41 +00:00
if ( isset ( $row -> page_len ) )
2012-01-03 19:28:03 +00:00
$this -> mLength = ( int ) $row -> page_len ;
2012-01-03 21:44:41 +00:00
if ( isset ( $row -> page_is_redirect ) )
2012-01-03 19:28:03 +00:00
$this -> mRedirect = ( bool ) $row -> page_is_redirect ;
2012-01-03 21:44:41 +00:00
if ( isset ( $row -> page_latest ) )
2012-01-03 19:28:03 +00:00
$this -> mLatestID = ( int ) $row -> page_latest ;
2011-07-01 23:33:34 +00:00
} else { // page not found
$this -> mArticleID = 0 ;
$this -> mLength = 0 ;
$this -> mRedirect = false ;
$this -> mLatestID = 0 ;
}
}
2004-09-30 05:21:20 +00:00
/**
* Create a new 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 .
2004-11-24 10:27:49 +00:00
* For convenience , spaces are converted to underscores so that
* eg user_text fields can be used directly .
*
2011-01-05 23:08:13 +00:00
* @ param $ns Int the namespace of the article
* @ param $title String the unprefixed database key form
2011-02-02 11:00:09 +00:00
* @ param $fragment String the link fragment ( after the " # " )
* @ param $interwiki String the interwiki prefix
2011-01-05 23:08:13 +00:00
* @ return Title the new object
2004-09-30 05:21:20 +00:00
*/
2010-07-08 10:49:36 +00:00
public static function & makeTitle ( $ns , $title , $fragment = '' , $interwiki = '' ) {
2006-07-11 14:11:23 +00:00
$t = new Title ();
2010-07-08 10:49:36 +00:00
$t -> mInterwiki = $interwiki ;
2008-07-24 16:25:19 +00:00
$t -> mFragment = $fragment ;
2007-05-19 01:38:25 +00:00
$t -> mNamespace = $ns = intval ( $ns );
2004-11-24 10:27:49 +00:00
$t -> mDbkeyform = str_replace ( ' ' , '_' , $title );
2004-09-11 09:35:24 +00:00
$t -> mArticleID = ( $ns >= 0 ) ? - 1 : 0 ;
2004-11-24 10:27:49 +00:00
$t -> mUrlform = wfUrlencode ( $t -> mDbkeyform );
2004-08-22 23:19:12 +00:00
$t -> mTextform = str_replace ( '_' , ' ' , $title );
return $t ;
2004-03-06 01:49:16 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2006-07-17 22:15:10 +00:00
* Create a new Title from a namespace index and a DB key .
2004-09-30 05:21:20 +00:00
* The parameters will be checked for validity , which is a bit slower
* than makeTitle () but safer for user - provided data .
2005-05-04 00:33:08 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $ns Int the namespace of the article
2011-02-02 11:00:09 +00:00
* @ param $title String database key form
* @ param $fragment String the link fragment ( after the " # " )
* @ param $interwiki String interwiki prefix
2011-01-05 23:08:13 +00:00
* @ return Title the new object , or NULL on an error
2004-09-30 05:21:20 +00:00
*/
2010-07-08 10:49:36 +00:00
public static function makeTitleSafe ( $ns , $title , $fragment = '' , $interwiki = '' ) {
2004-08-22 23:55:36 +00:00
$t = new Title ();
2010-07-08 10:49:36 +00:00
$t -> mDbkeyform = Title :: makeName ( $ns , $title , $fragment , $interwiki );
2010-04-24 06:22:11 +00:00
if ( $t -> secureAndSplit () ) {
2004-08-22 23:55:36 +00:00
return $t ;
} else {
2009-12-11 21:07:27 +00:00
return null ;
2004-08-22 23:55:36 +00:00
}
2009-12-14 20:37:51 +00:00
}
2003-04-14 23:10:40 +00:00
2004-09-30 05:21:20 +00:00
/**
* Create a new Title for the Main Page
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Title the new object
2004-09-30 05:21:20 +00:00
*/
2006-07-10 15:41:30 +00:00
public static function newMainPage () {
2008-02-13 06:13:03 +00:00
$title = Title :: newFromText ( wfMsgForContent ( 'mainpage' ) );
// Don't give fatal errors if the message is broken
if ( ! $title ) {
$title = Title :: newFromText ( 'Main Page' );
}
return $title ;
2004-03-06 01:49:16 +00:00
}
2004-08-07 03:50:46 +00:00
2004-09-30 05:21:20 +00:00
/**
2007-08-01 01:45:58 +00:00
* Extract a redirect destination from a string and return the
* Title , or null if the text doesn ' t contain a valid redirect
2009-01-29 00:29:52 +00:00
* This will only return the very next target , useful for
* the redirect table and other checks that don ' t need full recursion
2007-08-01 01:45:58 +00:00
*
2010-11-13 00:47:51 +00:00
* @ param $text String : Text with possible redirect
* @ return Title : The corresponding Title
2009-01-21 20:42:32 +00:00
*/
2009-01-29 00:29:52 +00:00
public static function newFromRedirect ( $text ) {
return self :: newFromRedirectInternal ( $text );
}
2009-12-14 20:37:51 +00:00
2009-01-29 00:29:52 +00:00
/**
* Extract a redirect destination from a string and return the
* Title , or null if the text doesn ' t contain a valid redirect
* This will recurse down $wgMaxRedirects times or until a non - redirect target is hit
* in order to provide ( hopefully ) the Title of the final destination instead of another redirect
*
2011-01-05 23:08:13 +00:00
* @ param $text String Text with possible redirect
2011-02-02 11:00:09 +00:00
* @ return Title
2009-01-29 00:29:52 +00:00
*/
public static function newFromRedirectRecurse ( $text ) {
$titles = self :: newFromRedirectArray ( $text );
2009-02-03 00:06:46 +00:00
return $titles ? array_pop ( $titles ) : null ;
2009-01-29 00:29:52 +00:00
}
2009-12-14 20:37:51 +00:00
2009-01-29 00:29:52 +00:00
/**
* Extract a redirect destination from a string and return an
* array of Titles , or null if the text doesn ' t contain a valid redirect
* The last element in the array is the final destination after all redirects
* have been resolved ( up to $wgMaxRedirects times )
*
2011-01-05 23:08:13 +00:00
* @ param $text String Text with possible redirect
* @ return Array of Titles , with the destination last
2009-01-29 00:29:52 +00:00
*/
public static function newFromRedirectArray ( $text ) {
2009-01-21 20:42:32 +00:00
global $wgMaxRedirects ;
2009-01-29 00:29:52 +00:00
$title = self :: newFromRedirectInternal ( $text );
2010-07-25 15:53:22 +00:00
if ( is_null ( $title ) ) {
2009-01-21 20:42:32 +00:00
return null ;
2010-07-25 15:53:22 +00:00
}
2009-01-29 00:29:52 +00:00
// recursive check to follow double redirects
$recurse = $wgMaxRedirects ;
$titles = array ( $title );
2010-04-24 06:22:11 +00:00
while ( -- $recurse > 0 ) {
if ( $title -> isRedirect () ) {
2011-11-02 16:36:20 +00:00
$page = WikiPage :: factory ( $title );
$newtitle = $page -> getRedirectTarget ();
2009-01-29 00:29:52 +00:00
} else {
break ;
}
// Redirects to some special pages are not permitted
2010-04-24 06:22:11 +00:00
if ( $newtitle instanceOf Title && $newtitle -> isValidRedirectTarget () ) {
2009-01-29 00:29:52 +00:00
// the new title passes the checks, so make that our current title so that further recursion can be checked
$title = $newtitle ;
$titles [] = $newtitle ;
} else {
break ;
}
}
return $titles ;
}
2009-12-14 20:37:51 +00:00
2009-01-29 00:29:52 +00:00
/**
* Really extract the redirect destination
* Do not call this function directly , use one of the newFromRedirect * functions above
*
2011-01-05 23:08:13 +00:00
* @ param $text String Text with possible redirect
2011-02-02 11:00:09 +00:00
* @ return Title
2009-01-29 00:29:52 +00:00
*/
protected static function newFromRedirectInternal ( $text ) {
2011-02-22 16:39:17 +00:00
global $wgMaxRedirects ;
if ( $wgMaxRedirects < 1 ) {
//redirects are disabled, so quit early
return null ;
}
2007-08-01 01:45:58 +00:00
$redir = MagicWord :: get ( 'redirect' );
2010-04-24 06:22:11 +00:00
$text = trim ( $text );
if ( $redir -> matchStartAndRemove ( $text ) ) {
2007-08-01 01:45:58 +00:00
// Extract the first link and see if it's usable
2008-08-06 20:33:45 +00:00
// Ensure that it really does come directly after #REDIRECT
2008-08-09 00:33:02 +00:00
// Some older redirects included a colon, so don't freak about that!
2007-08-21 03:57:54 +00:00
$m = array ();
2010-04-24 06:22:11 +00:00
if ( preg_match ( '!^\s*:?\s*\[{2}(.*?)(?:\|.*?)?\]{2}!' , $text , $m ) ) {
2007-08-01 01:45:58 +00:00
// Strip preceding colon used to "escape" categories, etc.
// and URL-decode links
2010-04-24 06:22:11 +00:00
if ( strpos ( $m [ 1 ], '%' ) !== false ) {
2007-08-26 14:52:56 +00:00
// Match behavior of inline link parsing here;
2010-12-24 09:53:08 +00:00
$m [ 1 ] = rawurldecode ( ltrim ( $m [ 1 ], ':' ) );
2007-08-26 14:52:56 +00:00
}
2007-08-01 01:45:58 +00:00
$title = Title :: newFromText ( $m [ 1 ] );
2009-01-29 00:29:52 +00:00
// If the title is a redirect to bad special pages or is invalid, return null
2010-04-24 06:22:11 +00:00
if ( ! $title instanceof Title || ! $title -> isValidRedirectTarget () ) {
2009-01-21 20:42:32 +00:00
return null ;
}
2009-01-29 00:29:52 +00:00
return $title ;
2004-08-07 03:50:46 +00:00
}
}
2007-08-01 01:45:58 +00:00
return null ;
2004-08-07 03:50:46 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get the prefixed DB key associated with an ID
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $id Int the page_id of the article
2011-02-02 11:00:09 +00:00
* @ return Title an object representing the article , or NULL if no such article was found
2004-09-30 05:21:20 +00:00
*/
2008-07-27 18:09:22 +00:00
public static function nameOf ( $id ) {
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2005-07-01 10:44:48 +00:00
2010-07-25 15:53:22 +00:00
$s = $dbr -> selectRow (
'page' ,
2010-04-24 06:22:11 +00:00
array ( 'page_namespace' , 'page_title' ),
2009-12-14 20:37:51 +00:00
array ( 'page_id' => $id ),
2010-07-25 15:53:22 +00:00
__METHOD__
);
if ( $s === false ) {
return null ;
}
2003-09-01 08:30:14 +00:00
2008-07-27 18:09:22 +00:00
$n = self :: makeName ( $s -> page_namespace , $s -> page_title );
2003-09-01 08:30:14 +00:00
return $n ;
}
2004-09-30 05:21:20 +00:00
/**
* Get a regex character class describing the legal characters in a link
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String the list of characters , not delimited
2004-09-30 05:21:20 +00:00
*/
2006-07-10 15:41:30 +00:00
public static function legalChars () {
2005-09-09 22:48:25 +00:00
global $wgLegalTitleChars ;
return $wgLegalTitleChars ;
2004-03-06 01:49:16 +00:00
}
2005-07-01 10:44:48 +00:00
2011-12-11 14:48:45 +00:00
/**
* Returns a simple regex that will match on characters and sequences invalid in titles .
* Note that this doesn ' t pick up many things that could be wrong with titles , but that
* replacing this regex with something valid will make many titles valid .
*
* @ return String regex string
*/
static function getTitleInvalidRegex () {
static $rxTc = false ;
if ( ! $rxTc ) {
# Matching titles will be held as illegal.
$rxTc = '/' .
# Any character not allowed is forbidden...
'[^' . self :: legalChars () . ']' .
# URL percent encoding sequences interfere with the ability
# to round-trip titles -- you can't link to them consistently.
'|%[0-9A-Fa-f]{2}' .
# XML/HTML character references produce similar issues.
'|&[A-Za-z0-9\x80-\xff]+;' .
'|&#[0-9]+;' .
'|&#x[0-9A-Fa-f]+;' .
'/S' ;
}
return $rxTc ;
}
2004-09-30 05:21:20 +00:00
/**
* Get a string representation of a title suitable for
* including in a search index
*
2011-01-05 23:08:13 +00:00
* @ param $ns Int a namespace index
* @ param $title String text - form main part
2011-02-02 11:00:09 +00:00
* @ return String a stripped - down title string ready for the search index
2004-09-30 05:21:20 +00:00
*/
2007-01-09 19:56:23 +00:00
public static function indexTitle ( $ns , $title ) {
2005-12-04 18:27:59 +00:00
global $wgContLang ;
2004-03-06 01:49:16 +00:00
2004-08-16 20:14:35 +00:00
$lc = SearchEngine :: legalSearchChars () . '&#;' ;
2010-02-02 15:09:01 +00:00
$t = $wgContLang -> normalizeForSearch ( $title );
2004-08-16 20:14:35 +00:00
$t = preg_replace ( " /[^ { $lc } ]+/ " , ' ' , $t );
2006-10-13 23:32:36 +00:00
$t = $wgContLang -> lc ( $t );
2004-03-06 01:49:16 +00:00
# Handle 's, s'
$t = preg_replace ( " /([ { $lc } ]+)'s( | $ )/ " , " \\ 1 \\ 1's " , $t );
$t = preg_replace ( " /([ { $lc } ]+)s'( | $ )/ " , " \\ 1s " , $t );
2004-08-16 20:14:35 +00:00
$t = preg_replace ( " / \\ s+/ " , ' ' , $t );
2004-03-06 01:49:16 +00:00
2008-12-01 17:14:30 +00:00
if ( $ns == NS_FILE ) {
2004-03-06 01:49:16 +00:00
$t = preg_replace ( " / (png|gif|jpg|jpeg|ogg) $ / " , " " , $t );
2003-05-16 13:36:37 +00:00
}
2004-03-06 01:49:16 +00:00
return trim ( $t );
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2009-06-02 19:37:45 +00:00
/**
2004-09-30 05:21:20 +00:00
* Make a prefixed DB key from a DB key and a namespace index
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $ns Int numerical representation of the namespace
* @ param $title String the DB key form the title
* @ param $fragment String The link fragment ( after the " # " )
* @ param $interwiki String The interwiki prefix
* @ return String the prefixed form of the title
2004-09-30 05:21:20 +00:00
*/
2010-07-08 10:49:36 +00:00
public static function makeName ( $ns , $title , $fragment = '' , $interwiki = '' ) {
2004-09-24 16:45:31 +00:00
global $wgContLang ;
2003-04-14 23:10:40 +00:00
2008-07-24 16:25:19 +00:00
$namespace = $wgContLang -> getNsText ( $ns );
$name = $namespace == '' ? $title : " $namespace : $title " ;
2010-07-08 10:49:36 +00:00
if ( strval ( $interwiki ) != '' ) {
$name = " $interwiki : $name " ;
}
2008-07-24 16:25:19 +00:00
if ( strval ( $fragment ) != '' ) {
$name .= '#' . $fragment ;
}
return $name ;
2004-03-06 01:49:16 +00:00
}
2005-07-01 10:44:48 +00:00
2011-12-10 15:25:28 +00:00
/**
* Escape a text fragment , say from a link , for a URL
*
* @ param $fragment string containing a URL or link fragment ( after the " # " )
* @ return String : escaped string
*/
static function escapeFragmentForURL ( $fragment ) {
# Note that we don't urlencode the fragment. urlencoded Unicode
# fragments appear not to work in IE (at least up to 7) or in at least
# one version of Opera 9.x. The W3C validator, for one, doesn't seem
# to care if they aren't encoded.
return Sanitizer :: escapeId ( $fragment , 'noninitial' );
}
2011-12-11 14:48:45 +00:00
/**
* Callback for usort () to do title sorts by ( namespace , title )
*
* @ param $a Title
* @ param $b Title
*
* @ return Integer : result of string comparison , or namespace comparison
*/
public static function compare ( $a , $b ) {
if ( $a -> getNamespace () == $b -> getNamespace () ) {
return strcmp ( $a -> getText (), $b -> getText () );
} else {
return $a -> getNamespace () - $b -> getNamespace ();
}
}
2004-09-30 05:21:20 +00:00
/**
* Determine whether the object refers to a page within
2005-07-01 10:44:48 +00:00
* this project .
*
2011-02-02 11:00:09 +00:00
* @ return Bool TRUE if this is an in - project interwiki link or a wikilink , FALSE otherwise
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function isLocal () {
2004-08-16 20:14:35 +00:00
if ( $this -> mInterwiki != '' ) {
2008-09-29 10:08:46 +00:00
return Interwiki :: fetch ( $this -> mInterwiki ) -> isLocal ();
2004-05-04 12:37:29 +00:00
} else {
return true ;
}
}
2011-12-10 15:25:28 +00:00
/**
* Is this Title interwiki ?
*
* @ return Bool
*/
public function isExternal () {
return ( $this -> mInterwiki != '' );
}
/**
* Get the interwiki prefix ( or null string )
*
* @ return String Interwiki prefix
*/
public function getInterwiki () {
return $this -> mInterwiki ;
}
2005-07-03 07:15:53 +00:00
/**
* Determine whether the object refers to a page within
* this project and is transcludable .
*
2011-01-05 23:08:13 +00:00
* @ return Bool TRUE if this is transcludable
2005-07-03 07:15:53 +00:00
*/
2007-04-16 15:24:04 +00:00
public function isTrans () {
2010-07-25 15:53:22 +00:00
if ( $this -> mInterwiki == '' ) {
2005-07-03 07:15:53 +00:00
return false ;
2010-07-25 15:53:22 +00:00
}
2009-12-14 20:37:51 +00:00
2008-09-29 10:08:46 +00:00
return Interwiki :: fetch ( $this -> mInterwiki ) -> isTranscludable ();
2005-07-03 07:15:53 +00:00
}
2010-07-19 11:55:30 +00:00
/**
2011-02-02 11:00:09 +00:00
* Returns the DB name of the distant wiki which owns the object .
2010-07-19 11:55:30 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String the DB name
2010-07-19 11:55:30 +00:00
*/
public function getTransWikiID () {
2010-07-25 15:53:22 +00:00
if ( $this -> mInterwiki == '' ) {
2010-07-19 11:55:30 +00:00
return false ;
2010-07-25 15:53:22 +00:00
}
2010-07-19 11:55:30 +00:00
return Interwiki :: fetch ( $this -> mInterwiki ) -> getWikiID ();
}
2004-09-30 05:21:20 +00:00
/**
* Get the text form ( spaces not underscores ) of the main part
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String Main part of the title
2004-09-30 05:21:20 +00:00
*/
2011-12-10 15:25:28 +00:00
public function getText () {
return $this -> mTextform ;
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get the URL - encoded form of the main part
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String Main part of the title , URL - encoded
2004-09-30 05:21:20 +00:00
*/
2011-12-10 15:25:28 +00:00
public function getPartialURL () {
return $this -> mUrlform ;
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get the main part with underscores
2010-03-23 21:40:02 +00:00
*
2010-11-13 12:50:50 +00:00
* @ return String : Main part of the title , with underscores
2004-09-30 05:21:20 +00:00
*/
2011-12-10 15:25:28 +00:00
public function getDBkey () {
return $this -> mDbkeyform ;
}
2010-03-23 21:40:02 +00:00
2011-12-11 14:48:45 +00:00
/**
* Get the DB key with the initial letter case as specified by the user
*
* @ return String DB key
*/
function getUserCaseDBKey () {
return $this -> mUserCaseDBKey ;
}
2004-09-30 05:21:20 +00:00
/**
2011-02-02 11:00:09 +00:00
* Get the namespace index , i . e . one of the NS_xxxx constants .
2010-03-23 21:40:02 +00:00
*
2010-11-13 00:47:51 +00:00
* @ return Integer : Namespace index
2004-09-30 05:21:20 +00:00
*/
2011-12-10 15:25:28 +00:00
public function getNamespace () {
return $this -> mNamespace ;
}
2010-03-23 21:40:02 +00:00
2005-08-27 03:47:47 +00:00
/**
* Get the namespace text
2010-03-23 21:40:02 +00:00
*
2010-11-13 00:47:51 +00:00
* @ return String : Namespace text
2005-08-27 03:47:47 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getNsText () {
2009-10-16 04:06:30 +00:00
global $wgContLang ;
2006-12-31 03:04:49 +00:00
2010-01-06 19:59:42 +00:00
if ( $this -> mInterwiki != '' ) {
2006-12-31 03:04:49 +00:00
// This probably shouldn't even happen. ohh man, oh yuck.
// But for interwiki transclusion it sometimes does.
// Shit. Shit shit shit.
//
// Use the canonical namespaces if possible to try to
// resolve a foreign namespace.
2010-04-24 06:22:11 +00:00
if ( MWNamespace :: exists ( $this -> mNamespace ) ) {
2009-10-16 04:06:30 +00:00
return MWNamespace :: getCanonicalName ( $this -> mNamespace );
2006-12-31 03:04:49 +00:00
}
}
2011-02-12 20:40:40 +00:00
2011-09-19 11:49:22 +00:00
// Strip off subpages
$pagename = $this -> getText ();
if ( strpos ( $pagename , '/' ) !== false ) {
list ( $username , ) = explode ( '/' , $pagename , 2 );
} else {
$username = $pagename ;
}
2011-02-12 20:40:40 +00:00
if ( $wgContLang -> needsGenderDistinction () &&
MWNamespace :: hasGenderDistinction ( $this -> mNamespace ) ) {
2011-09-19 11:49:22 +00:00
$gender = GenderCache :: singleton () -> getGenderOf ( $username , __METHOD__ );
2011-02-12 20:40:40 +00:00
return $wgContLang -> getGenderNsText ( $this -> mNamespace , $gender );
}
2005-08-27 03:56:58 +00:00
return $wgContLang -> getNsText ( $this -> mNamespace );
}
2010-03-23 21:40:02 +00:00
2005-08-27 03:56:58 +00:00
/**
* Get the namespace text of the subject ( rather than talk ) page
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String Namespace text
2005-08-27 03:56:58 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getSubjectNsText () {
2009-02-17 23:00:57 +00:00
global $wgContLang ;
return $wgContLang -> getNsText ( MWNamespace :: getSubject ( $this -> mNamespace ) );
2005-08-27 03:47:47 +00:00
}
2010-03-23 21:40:02 +00:00
2006-04-12 15:38:17 +00:00
/**
* Get the namespace text of the talk page
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String Namespace text
2006-04-12 15:38:17 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getTalkNsText () {
2009-02-17 23:00:57 +00:00
global $wgContLang ;
return ( $wgContLang -> getNsText ( MWNamespace :: getTalk ( $this -> mNamespace ) ) );
2006-04-12 15:38:17 +00:00
}
2010-03-23 21:40:02 +00:00
2006-04-12 15:38:17 +00:00
/**
* Could this title have a corresponding talk page ?
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Bool TRUE or FALSE
2006-04-12 15:38:17 +00:00
*/
2007-04-16 15:24:04 +00:00
public function canTalk () {
2008-03-21 23:13:34 +00:00
return ( MWNamespace :: canTalk ( $this -> mNamespace ) );
2006-04-12 15:38:17 +00:00
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Is this in a namespace that allows actual pages ?
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool
* @ internal note -- uses hardcoded namespace index instead of constants
2006-12-08 06:09:15 +00:00
*/
2011-12-11 14:48:45 +00:00
public function canExist () {
2011-12-26 08:07:56 +00:00
return $this -> mNamespace >= NS_MAIN ;
2006-12-08 06:09:15 +00:00
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Can this title be added to a user ' s watchlist ?
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool TRUE or FALSE
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isWatchable () {
return ! $this -> isExternal () && MWNamespace :: isWatchable ( $this -> getNamespace () );
2011-12-10 15:25:28 +00:00
}
2003-04-14 23:10:40 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Returns true if this is a special page .
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return boolean
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isSpecialPage () {
return $this -> getNamespace () == NS_SPECIAL ;
2003-04-14 23:10:40 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Returns true if this title resolves to the named special page
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param $name String The special page name
* @ return boolean
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isSpecial ( $name ) {
if ( $this -> isSpecialPage () ) {
list ( $thisName , /* $subpage */ ) = SpecialPageFactory :: resolveAlias ( $this -> getDBkey () );
if ( $name == $thisName ) {
return true ;
}
}
return false ;
2003-04-14 23:10:40 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* If the Title refers to a special page alias which is not the local default , resolve
* the alias , and localise the name as necessary . Otherwise , return $this
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Title
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function fixSpecialName () {
if ( $this -> isSpecialPage () ) {
list ( $canonicalName , $par ) = SpecialPageFactory :: resolveAlias ( $this -> mDbkeyform );
if ( $canonicalName ) {
$localName = SpecialPageFactory :: getLocalNameFor ( $canonicalName , $par );
if ( $localName != $this -> mDbkeyform ) {
return Title :: makeTitle ( NS_SPECIAL , $localName );
}
}
2003-10-22 23:56:49 +00:00
}
2011-12-11 14:48:45 +00:00
return $this ;
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2011-08-24 13:03:03 +00:00
/**
2011-12-11 14:48:45 +00:00
* Returns true if the title is inside the specified namespace .
*
* Please make use of this instead of comparing to getNamespace ()
* This function is much more resistant to changes we may make
* to namespaces than code that makes direct comparisons .
* @ param $ns int The namespace
* @ return bool
* @ since 1.19
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function inNamespace ( $ns ) {
return MWNamespace :: equals ( $this -> getNamespace (), $ns );
2004-07-29 04:48:42 +00:00
}
2003-04-14 23:10:40 +00:00
2006-05-14 03:51:36 +00:00
/**
2011-12-11 14:48:45 +00:00
* Returns true if the title is inside one of the specified namespaces .
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param ... $namespaces The namespaces to check for
* @ return bool
* @ since 1.19
2006-05-14 03:51:36 +00:00
*/
2011-12-11 14:48:45 +00:00
public function inNamespaces ( /* ... */ ) {
$namespaces = func_get_args ();
if ( count ( $namespaces ) > 0 && is_array ( $namespaces [ 0 ] ) ) {
$namespaces = $namespaces [ 0 ];
2006-05-14 03:51:36 +00:00
}
2008-05-23 22:00:14 +00:00
2011-12-11 14:48:45 +00:00
foreach ( $namespaces as $ns ) {
if ( $this -> inNamespace ( $ns ) ) {
return true ;
}
2010-07-25 15:53:22 +00:00
}
2011-12-11 14:48:45 +00:00
return false ;
2008-02-04 19:53:21 +00:00
}
2006-05-14 03:51:36 +00:00
2006-03-04 23:29:46 +00:00
/**
2011-12-11 14:48:45 +00:00
* Returns true if the title has the same subject namespace as the
* namespace specified .
* For example this method will take NS_USER and return true if namespace
* is either NS_USER or NS_USER_TALK since both of them have NS_USER
* as their subject namespace .
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* This is MUCH simpler than individually testing for equivilance
* against both NS_USER and NS_USER_TALK , and is also forward compatible .
* @ since 1.19
2012-02-09 21:36:14 +00:00
* @ return bool
2006-03-04 23:29:46 +00:00
*/
2011-12-11 14:48:45 +00:00
public function hasSubjectNamespace ( $ns ) {
return MWNamespace :: subjectEquals ( $this -> getNamespace (), $ns );
2006-03-04 23:29:46 +00:00
}
2007-04-16 15:24:04 +00:00
2011-12-10 15:25:28 +00:00
/**
2011-12-11 14:48:45 +00:00
* Is this Title in a namespace which contains content ?
* In other words , is this a content page , for the purposes of calculating
* statistics , etc ?
2011-12-10 15:25:28 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Boolean
2011-12-10 15:25:28 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isContentPage () {
return MWNamespace :: isContent ( $this -> getNamespace () );
2011-12-10 15:25:28 +00:00
}
2006-04-02 16:19:29 +00:00
/**
2011-12-11 14:48:45 +00:00
* Would anybody with sufficient privileges be able to move this page ?
* Some pages just aren ' t movable .
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool TRUE or FALSE
2006-04-02 16:19:29 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isMovable () {
if ( ! MWNamespace :: isMovable ( $this -> getNamespace () ) || $this -> getInterwiki () != '' ) {
// Interwiki title or immovable namespace. Hooks don't get to override here
return false ;
}
$result = true ;
wfRunHooks ( 'TitleIsMovable' , array ( $this , & $result ) );
return $result ;
2006-04-02 16:19:29 +00:00
}
2006-03-04 23:29:46 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Is this the mainpage ?
* @ note Title :: newFromText seams to be sufficiently optimized by the title
* cache that we don ' t need to over - optimize by doing direct comparisons and
* acidentally creating new bugs where $title -> equals ( Title :: newFromText () )
* ends up reporting something differently than $title -> isMainPage ();
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ since 1.18
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isMainPage () {
return $this -> equals ( Title :: newMainPage () );
2003-04-14 23:10:40 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Is this a subpage ?
2004-09-30 05:21:20 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isSubpage () {
return MWNamespace :: hasSubpages ( $this -> mNamespace )
? strpos ( $this -> getText (), '/' ) !== false
: false ;
}
/**
* Is this a conversion table for the LanguageConverter ?
*
* @ return Bool
*/
public function isConversionTable () {
return $this -> getNamespace () == NS_MEDIAWIKI &&
strpos ( $this -> getText (), 'Conversiontable' ) !== false ;
}
/**
* Does that page contain wikitext , or it is JS , CSS or whatever ?
*
* @ return Bool
*/
public function isWikitextPage () {
$retval = ! $this -> isCssOrJsPage () && ! $this -> isCssJsSubpage ();
wfRunHooks ( 'TitleIsWikitextPage' , array ( $this , & $retval ) );
return $retval ;
}
/**
* Could this page contain custom CSS or JavaScript , based
* on the title ?
*
* @ return Bool
*/
public function isCssOrJsPage () {
$retval = $this -> mNamespace == NS_MEDIAWIKI
&& preg_match ( '!\.(?:css|js)$!u' , $this -> mTextform ) > 0 ;
wfRunHooks ( 'TitleIsCssOrJsPage' , array ( $this , & $retval ) );
return $retval ;
}
/**
* Is this a . css or . js subpage of a user page ?
* @ return Bool
*/
public function isCssJsSubpage () {
return ( NS_USER == $this -> mNamespace and preg_match ( " / \\ /.* \\ .(?:css|js) $ / " , $this -> mTextform ) );
}
/**
* Trim down a . css or . js subpage title to get the corresponding skin name
*
* @ return string containing skin name from . css or . js subpage title
*/
public function getSkinFromCssJsSubpage () {
$subpage = explode ( '/' , $this -> mTextform );
$subpage = $subpage [ count ( $subpage ) - 1 ];
$lastdot = strrpos ( $subpage , '.' );
if ( $lastdot === false )
return $subpage ; # Never happens: only called for names ending in '.css' or '.js'
return substr ( $subpage , 0 , $lastdot );
}
/**
* Is this a . css subpage of a user page ?
*
* @ return Bool
*/
public function isCssSubpage () {
return ( NS_USER == $this -> mNamespace && preg_match ( " / \\ /.* \\ .css $ / " , $this -> mTextform ) );
}
/**
* Is this a . js subpage of a user page ?
*
* @ return Bool
*/
public function isJsSubpage () {
return ( NS_USER == $this -> mNamespace && preg_match ( " / \\ /.* \\ .js $ / " , $this -> mTextform ) );
}
/**
* Is this a talk page of some sort ?
*
* @ return Bool
*/
public function isTalkPage () {
return MWNamespace :: isTalk ( $this -> getNamespace () );
}
/**
* Get a Title object associated with the talk page of this article
*
* @ return Title the object for the talk page
*/
public function getTalkPage () {
return Title :: makeTitle ( MWNamespace :: getTalk ( $this -> getNamespace () ), $this -> getDBkey () );
}
/**
* Get a title object associated with the subject page of this
* talk page
*
* @ return Title the object for the subject page
*/
public function getSubjectPage () {
// Is this the same title?
$subjectNS = MWNamespace :: getSubject ( $this -> getNamespace () );
if ( $this -> getNamespace () == $subjectNS ) {
return $this ;
}
return Title :: makeTitle ( $subjectNS , $this -> getDBkey () );
}
/**
* Get the default namespace index , for when there is no namespace
*
* @ return Int Default namespace index
*/
public function getDefaultNamespace () {
return $this -> mDefaultNamespace ;
}
/**
* Get title for search index
*
* @ return String a stripped - down title string ready for the
* search index
*/
public function getIndexTitle () {
return Title :: indexTitle ( $this -> mNamespace , $this -> mTextform );
}
/**
* Get the Title fragment ( i . e . \ the bit after the #) in text form
*
* @ return String Title fragment
*/
public function getFragment () {
return $this -> mFragment ;
}
/**
* Get the fragment in URL form , including the " # " character if there is one
* @ return String Fragment in URL form
*/
public function getFragmentForURL () {
if ( $this -> mFragment == '' ) {
return '' ;
} else {
return '#' . Title :: escapeFragmentForURL ( $this -> mFragment );
}
}
/**
* Set the fragment for this title . Removes the first character from the
* specified fragment before setting , so it assumes you ' re passing it with
* an initial " # " .
*
* Deprecated for public use , use Title :: makeTitle () with fragment parameter .
* Still in active use privately .
*
* @ param $fragment String text
*/
public function setFragment ( $fragment ) {
$this -> mFragment = str_replace ( '_' , ' ' , substr ( $fragment , 1 ) );
}
/**
* Prefix some arbitrary text with the namespace or interwiki prefix
* of this object
*
* @ param $name String the text
* @ return String the prefixed text
* @ private
*/
private function prefix ( $name ) {
$p = '' ;
if ( $this -> mInterwiki != '' ) {
$p = $this -> mInterwiki . ':' ;
}
if ( 0 != $this -> mNamespace ) {
$p .= $this -> getNsText () . ':' ;
}
return $p . $name ;
}
/**
* Get the prefixed database key form
*
* @ return String the prefixed title , with underscores and
* any interwiki and namespace prefixes
*/
public function getPrefixedDBkey () {
$s = $this -> prefix ( $this -> mDbkeyform );
$s = str_replace ( ' ' , '_' , $s );
return $s ;
}
/**
* Get the prefixed title with spaces .
* This is the form usually used for display
*
* @ return String the prefixed title , with spaces
*/
public function getPrefixedText () {
// @todo FIXME: Bad usage of empty() ?
if ( empty ( $this -> mPrefixedText ) ) {
$s = $this -> prefix ( $this -> mTextform );
$s = str_replace ( '_' , ' ' , $s );
$this -> mPrefixedText = $s ;
}
return $this -> mPrefixedText ;
}
/**
* Return a string representation of this title
*
* @ return String representation of this title
*/
public function __toString () {
return $this -> getPrefixedText ();
}
/**
* Get the prefixed title with spaces , plus any fragment
* ( part beginning with '#' )
*
* @ return String the prefixed title , with spaces and the fragment , including '#'
*/
public function getFullText () {
$text = $this -> getPrefixedText ();
if ( $this -> mFragment != '' ) {
$text .= '#' . $this -> mFragment ;
}
return $text ;
}
/**
* Get the base page name , i . e . the leftmost part before any slashes
*
* @ return String Base name
*/
public function getBaseText () {
if ( ! MWNamespace :: hasSubpages ( $this -> mNamespace ) ) {
return $this -> getText ();
}
$parts = explode ( '/' , $this -> getText () );
# Don't discard the real title if there's no subpage involved
if ( count ( $parts ) > 1 ) {
unset ( $parts [ count ( $parts ) - 1 ] );
}
return implode ( '/' , $parts );
}
/**
* Get the lowest - level subpage name , i . e . the rightmost part after any slashes
*
* @ return String Subpage name
*/
public function getSubpageText () {
if ( ! MWNamespace :: hasSubpages ( $this -> mNamespace ) ) {
return ( $this -> mTextform );
}
$parts = explode ( '/' , $this -> mTextform );
return ( $parts [ count ( $parts ) - 1 ] );
}
/**
* Get the HTML - escaped displayable text form .
* Used for the title field in < a > tags .
*
* @ return String the text , including any prefixes
*/
public function getEscapedText () {
2011-12-12 19:23:38 +00:00
wfDeprecated ( __METHOD__ , '1.19' );
2011-12-11 14:48:45 +00:00
return htmlspecialchars ( $this -> getPrefixedText () );
}
/**
* Get a URL - encoded form of the subpage text
*
* @ return String URL - encoded subpage name
*/
public function getSubpageUrlForm () {
$text = $this -> getSubpageText ();
$text = wfUrlencode ( str_replace ( ' ' , '_' , $text ) );
return ( $text );
}
/**
* Get a URL - encoded title ( not an actual URL ) including interwiki
*
* @ return String the URL - encoded form
*/
public function getPrefixedURL () {
$s = $this -> prefix ( $this -> mDbkeyform );
$s = wfUrlencode ( str_replace ( ' ' , '_' , $s ) );
return $s ;
}
2011-12-12 19:19:23 +00:00
/**
* Helper to fix up the get { Local , Full , Link , Canonical } URL args
2012-01-17 16:42:53 +00:00
* get { Canonical , Full , Link , Local } URL methods accepted an optional
* second argument named variant . This was deprecated in favor
* of passing an array of option with a " variant " key
* Once $query2 is removed for good , this helper can be dropped
* andthe wfArrayToCGI moved to getLocalURL ();
*
* @ since 1.19 ( r105919 )
2012-02-09 21:36:14 +00:00
* @ return String
2011-12-12 19:19:23 +00:00
*/
2012-01-17 16:42:53 +00:00
private static function fixUrlQueryArgs ( $query , $query2 = false ) {
if ( $query2 !== false ) {
2012-01-27 13:27:26 +00:00
wfDeprecated ( " Title::get { Canonical,Full,Link,Local} method called with a second parameter is deprecated. Add your parameter to an array passed as the first parameter. " , " 1.19 " );
2012-01-17 16:42:53 +00:00
}
2011-12-12 19:19:23 +00:00
if ( is_array ( $query ) ) {
$query = wfArrayToCGI ( $query );
}
if ( $query2 ) {
if ( is_string ( $query2 ) ) {
// $query2 is a string, we will consider this to be
// a deprecated $variant argument and add it to the query
$query2 = wfArrayToCGI ( array ( 'variant' => $query2 ) );
} else {
$query2 = wfArrayToCGI ( $query2 );
}
// If we have $query content add a & to it first
if ( $query ) {
$query .= '&' ;
}
// Now append the queries together
$query .= $query2 ;
}
return $query ;
}
2011-12-11 14:48:45 +00:00
/**
* Get a real URL referring to this title , with interwiki link and
* fragment
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-12-11 14:48:45 +00:00
* @ return String the URL
*/
2011-12-12 19:19:23 +00:00
public function getFullURL ( $query = '' , $query2 = false ) {
$query = self :: fixUrlQueryArgs ( $query , $query2 );
2011-12-11 14:48:45 +00:00
# Hand off all the decisions on urls to getLocalURL
2011-12-12 19:19:23 +00:00
$url = $this -> getLocalURL ( $query );
2011-12-11 14:48:45 +00:00
2011-08-12 19:55:03 +00:00
# Expand the url to make it a full url. Note that getLocalURL has the
# potential to output full urls for a variety of reasons, so we use
# wfExpandUrl instead of simply prepending $wgServer
2011-08-14 14:07:12 +00:00
$url = wfExpandUrl ( $url , PROTO_RELATIVE );
2011-08-18 23:37:04 +00:00
2006-05-01 16:06:14 +00:00
# Finally, add the fragment.
2006-12-08 06:09:15 +00:00
$url .= $this -> getFragmentForURL ();
2006-05-01 16:06:14 +00:00
2011-12-12 19:19:23 +00:00
wfRunHooks ( 'GetFullURL' , array ( & $this , & $url , $query ) );
2005-10-22 16:25:05 +00:00
return $url ;
2005-03-27 16:05:33 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
2005-07-04 04:20:07 +00:00
* Get a URL with no fragment or server name . If this page is generated
* with action = render , $wgServer is prepended .
2010-03-23 21:40:02 +00:00
*
2011-12-12 20:03:50 +00:00
2012-02-09 19:29:36 +00:00
* @ param $query string | array an optional query string ,
2011-12-12 20:03:50 +00:00
* not used for interwiki links . Can be specified as an associative array as well ,
* e . g . , array ( 'action' => 'edit' ) ( keys and values will be URL - escaped ) .
* Some query patterns will trigger various shorturl path replacements .
* @ param $query2 Mixed : An optional secondary query array . This one MUST
* be an array . If a string is passed it will be interpreted as a deprecated
* variant argument and urlencoded into a variant = argument .
* This second query argument will be added to the $query
2012-01-17 16:42:53 +00:00
* The second parameter is deprecated since 1.19 . Pass it as a key , value
* pair in the first parameter array instead .
*
2011-01-05 23:08:13 +00:00
* @ return String the URL
2004-09-30 05:21:20 +00:00
*/
2011-12-12 19:19:23 +00:00
public function getLocalURL ( $query = '' , $query2 = false ) {
2005-10-22 16:25:05 +00:00
global $wgArticlePath , $wgScript , $wgServer , $wgRequest ;
2008-05-14 18:28:52 +00:00
2011-12-12 19:19:23 +00:00
$query = self :: fixUrlQueryArgs ( $query , $query2 );
2008-08-01 00:47:26 +00:00
2011-08-12 19:55:03 +00:00
$interwiki = Interwiki :: fetch ( $this -> mInterwiki );
if ( $interwiki ) {
$namespace = $this -> getNsText ();
if ( $namespace != '' ) {
# Can this actually happen? Interwikis shouldn't be parsed.
# Yes! It can in interwiki transclusion. But... it probably shouldn't.
$namespace .= ':' ;
2006-02-01 04:41:53 +00:00
}
2011-08-12 19:55:03 +00:00
$url = $interwiki -> getURL ( $namespace . $this -> getDBkey () );
$url = wfAppendQuery ( $url , $query );
2004-03-06 01:49:16 +00:00
} else {
2005-10-22 16:25:05 +00:00
$dbkey = wfUrlencode ( $this -> getPrefixedDBkey () );
2008-09-29 23:02:52 +00:00
if ( $query == '' ) {
2011-12-12 19:19:23 +00:00
$url = str_replace ( '$1' , $dbkey , $wgArticlePath );
wfRunHooks ( 'GetLocalURL::Article' , array ( & $this , & $url ) );
2005-10-22 16:25:05 +00:00
} else {
2011-12-12 19:19:23 +00:00
global $wgVariantArticlePath , $wgActionPaths ;
2011-05-23 19:03:17 +00:00
$url = false ;
$matches = array ();
2011-12-12 19:19:23 +00:00
2011-05-23 19:03:17 +00:00
if ( ! empty ( $wgActionPaths ) &&
preg_match ( '/^(.*&|)action=([^&]*)(&(.*)|)$/' , $query , $matches ) )
{
$action = urldecode ( $matches [ 2 ] );
if ( isset ( $wgActionPaths [ $action ] ) ) {
$query = $matches [ 1 ];
if ( isset ( $matches [ 4 ] ) ) {
$query .= $matches [ 4 ];
}
$url = str_replace ( '$1' , $dbkey , $wgActionPaths [ $action ] );
if ( $query != '' ) {
$url = wfAppendQuery ( $url , $query );
}
}
2005-10-22 16:25:05 +00:00
}
2011-05-13 11:41:17 +00:00
2011-12-12 19:19:23 +00:00
if ( $url === false &&
$wgVariantArticlePath &&
$this -> getPageLanguage () -> hasVariants () &&
preg_match ( '/^variant=([^&]*)$/' , $query , $matches ) )
{
$variant = urldecode ( $matches [ 1 ] );
if ( $this -> getPageLanguage () -> hasVariant ( $variant ) ) {
// Only do the variant replacement if the given variant is a valid
// variant for the page's language.
$url = str_replace ( '$2' , urlencode ( $variant ), $wgVariantArticlePath );
$url = str_replace ( '$1' , $dbkey , $url );
}
}
2005-10-22 16:25:05 +00:00
if ( $url === false ) {
if ( $query == '-' ) {
$query = '' ;
}
2011-05-23 19:03:17 +00:00
$url = " { $wgScript } ?title= { $dbkey } & { $query } " ;
2005-02-28 07:07:14 +00:00
}
}
2011-08-18 23:37:04 +00:00
2011-12-12 19:19:23 +00:00
wfRunHooks ( 'GetLocalURL::Internal' , array ( & $this , & $url , $query ) );
2008-05-09 23:52:04 +00:00
2011-05-17 22:03:20 +00:00
// @todo FIXME: This causes breakage in various places when we
2006-01-12 10:06:54 +00:00
// actually expected a local URL and end up with dupe prefixes.
2010-04-24 06:22:11 +00:00
if ( $wgRequest -> getVal ( 'action' ) == 'render' ) {
2005-10-22 16:25:05 +00:00
$url = $wgServer . $url ;
2004-03-06 01:49:16 +00:00
}
}
2011-12-12 19:19:23 +00:00
wfRunHooks ( 'GetLocalURL' , array ( & $this , & $url , $query ) );
2011-05-13 11:41:17 +00:00
return $url ;
}
/**
2008-08-01 15:02:46 +00:00
* Get a URL that ' s the simplest URL that will be valid to link , locally ,
* to the current Title . It includes the fragment , but does not include
* the server unless action = render is used ( or the link is external ) . If
* there ' s a fragment but the prefixed text is empty , we just return a link
* to the fragment .
*
2009-04-19 17:07:41 +00:00
* The result obviously should not be URL - escaped , but does need to be
* HTML - escaped if it ' s being output in HTML .
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-01-05 23:08:13 +00:00
* @ return String the URL
2008-08-01 15:02:46 +00:00
*/
2011-12-12 19:19:23 +00:00
public function getLinkURL ( $query = '' , $query2 = false ) {
2009-02-20 11:09:04 +00:00
wfProfileIn ( __METHOD__ );
2010-04-24 06:22:11 +00:00
if ( $this -> isExternal () ) {
2011-12-12 19:19:23 +00:00
$ret = $this -> getFullURL ( $query , $query2 );
2010-04-24 06:22:11 +00:00
} elseif ( $this -> getPrefixedText () === '' && $this -> getFragment () !== '' ) {
2009-02-20 14:13:43 +00:00
$ret = $this -> getFragmentForURL ();
2008-08-01 15:02:46 +00:00
} else {
2011-12-12 19:19:23 +00:00
$ret = $this -> getLocalURL ( $query , $query2 ) . $this -> getFragmentForURL ();
2008-08-01 15:02:46 +00:00
}
2009-02-20 11:09:04 +00:00
wfProfileOut ( __METHOD__ );
2009-02-20 14:13:43 +00:00
return $ret ;
2008-08-01 15:02:46 +00:00
}
2004-09-30 05:21:20 +00:00
/**
* Get an HTML - escaped version of the URL form , suitable for
* using in a link , without a server name or fragment
2010-03-23 21:40:02 +00:00
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-01-05 23:08:13 +00:00
* @ return String the URL
2004-09-30 05:21:20 +00:00
*/
2011-12-12 19:19:23 +00:00
public function escapeLocalURL ( $query = '' , $query2 = false ) {
2011-12-12 19:23:38 +00:00
wfDeprecated ( __METHOD__ , '1.19' );
2011-12-12 19:19:23 +00:00
return htmlspecialchars ( $this -> getLocalURL ( $query , $query2 ) );
2004-03-07 07:26:56 +00:00
}
2004-09-30 05:21:20 +00:00
/**
* Get an HTML - escaped version of the URL form , suitable for
* using in a link , including the server name and fragment
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-01-05 23:08:13 +00:00
* @ return String the URL
2004-09-30 05:21:20 +00:00
*/
2011-12-12 19:19:23 +00:00
public function escapeFullURL ( $query = '' , $query2 = false ) {
2011-12-12 19:23:38 +00:00
wfDeprecated ( __METHOD__ , '1.19' );
2011-12-12 19:19:23 +00:00
return htmlspecialchars ( $this -> getFullURL ( $query , $query2 ) );
2004-03-07 07:26:56 +00:00
}
2011-09-23 20:20:41 +00:00
2005-07-01 10:44:48 +00:00
/**
2004-09-30 05:21:20 +00:00
* Get the URL form for an internal link .
* - Used in various Squid - related code , in case we have a different
* internal hostname for the server from the exposed one .
2011-09-23 20:20:41 +00:00
*
2011-08-19 08:56:39 +00:00
* This uses $wgInternalServer to qualify the path , or $wgServer
* if $wgInternalServer is not set . If the server variable used is
* protocol - relative , the URL will be expanded to http ://
2004-09-30 05:21:20 +00:00
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-01-05 23:08:13 +00:00
* @ return String the URL
2004-09-30 05:21:20 +00:00
*/
2011-12-12 19:19:23 +00:00
public function getInternalURL ( $query = '' , $query2 = false ) {
2011-09-29 22:08:00 +00:00
global $wgInternalServer , $wgServer ;
2011-12-12 19:19:23 +00:00
$query = self :: fixUrlQueryArgs ( $query , $query2 );
2011-09-29 22:08:00 +00:00
$server = $wgInternalServer !== false ? $wgInternalServer : $wgServer ;
2011-12-12 19:19:23 +00:00
$url = wfExpandUrl ( $server . $this -> getLocalURL ( $query ), PROTO_HTTP );
wfRunHooks ( 'GetInternalURL' , array ( & $this , & $url , $query ) );
2005-12-26 07:14:42 +00:00
return $url ;
2004-03-07 07:26:56 +00:00
}
2004-03-06 01:49:16 +00:00
Per CR on r44412 and my promise in the commit summary of r94990, stop abusing $wgInternalServer (intended for Squid URLs) for IRC/e-mail URLs and introduce $wgCanonicalServer for these purposes instead. This revision introduces two new hooks for WMF hacks, in exchange for making the core code saner.
* Introduce $wgCanonicalServer, which should typically be a fully qualified version of $wgServer but in practice can be anything that you'd like to be used in IRC/e-mail notifs
** Default value is $wgServer, expanded to http:// if protocol-relative
** This means you can easily set HTTPS as the 'default' protocol to use in IRC and e-mail notifs by setting $wgCanonicalServer to https://example.com
* Introduce Title::getCanonicalURL(). Similar to getInternalURL(), including a hook for WMF usage (which will be needed as long as secure.wikimedia.org is used)
** Also add escapeCanonicalURL(). Due to some ridiculous accident of history, the other escapeFooURL() functions don't have a $variant parameter; I decided not to follow that bad example
* Reinstate the spirit of r44406 and r44412: instead of calling getInternalURL() (or getCanonicalURL()) and regexing the title parameter out, obtain the path to index.php using $wgCanonicalServer . $wgScript and append params to that. Sadly, we need to add a hook here to support the secure server hack for WMF, but that's the price of saner code in this case
* Introduce the {{canonicalurl:}} and {{canonicalurle:}} parser functions, which work just like {{fullurl:}} and {{fullurle:}} except that they use getCanonicalURL() instead of getFullURL()
* Use {{canonicalurl:}} in the enotif_body message, fixing bug 29993 (protocol-relative URLs appear in e-mail notifications)
2011-08-19 11:23:17 +00:00
/**
* Get the URL for a canonical link , for use in things like IRC and
* e - mail notifications . Uses $wgCanonicalServer and the
* GetCanonicalURL hook .
2011-09-23 20:20:41 +00:00
*
2011-08-28 15:15:42 +00:00
* NOTE : Unlike getInternalURL (), the canonical URL includes the fragment
2011-09-23 20:20:41 +00:00
*
2011-12-12 20:03:50 +00:00
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
Per CR on r44412 and my promise in the commit summary of r94990, stop abusing $wgInternalServer (intended for Squid URLs) for IRC/e-mail URLs and introduce $wgCanonicalServer for these purposes instead. This revision introduces two new hooks for WMF hacks, in exchange for making the core code saner.
* Introduce $wgCanonicalServer, which should typically be a fully qualified version of $wgServer but in practice can be anything that you'd like to be used in IRC/e-mail notifs
** Default value is $wgServer, expanded to http:// if protocol-relative
** This means you can easily set HTTPS as the 'default' protocol to use in IRC and e-mail notifs by setting $wgCanonicalServer to https://example.com
* Introduce Title::getCanonicalURL(). Similar to getInternalURL(), including a hook for WMF usage (which will be needed as long as secure.wikimedia.org is used)
** Also add escapeCanonicalURL(). Due to some ridiculous accident of history, the other escapeFooURL() functions don't have a $variant parameter; I decided not to follow that bad example
* Reinstate the spirit of r44406 and r44412: instead of calling getInternalURL() (or getCanonicalURL()) and regexing the title parameter out, obtain the path to index.php using $wgCanonicalServer . $wgScript and append params to that. Sadly, we need to add a hook here to support the secure server hack for WMF, but that's the price of saner code in this case
* Introduce the {{canonicalurl:}} and {{canonicalurle:}} parser functions, which work just like {{fullurl:}} and {{fullurle:}} except that they use getCanonicalURL() instead of getFullURL()
* Use {{canonicalurl:}} in the enotif_body message, fixing bug 29993 (protocol-relative URLs appear in e-mail notifications)
2011-08-19 11:23:17 +00:00
* @ return string The URL
2011-11-06 12:15:28 +00:00
* @ since 1.18
Per CR on r44412 and my promise in the commit summary of r94990, stop abusing $wgInternalServer (intended for Squid URLs) for IRC/e-mail URLs and introduce $wgCanonicalServer for these purposes instead. This revision introduces two new hooks for WMF hacks, in exchange for making the core code saner.
* Introduce $wgCanonicalServer, which should typically be a fully qualified version of $wgServer but in practice can be anything that you'd like to be used in IRC/e-mail notifs
** Default value is $wgServer, expanded to http:// if protocol-relative
** This means you can easily set HTTPS as the 'default' protocol to use in IRC and e-mail notifs by setting $wgCanonicalServer to https://example.com
* Introduce Title::getCanonicalURL(). Similar to getInternalURL(), including a hook for WMF usage (which will be needed as long as secure.wikimedia.org is used)
** Also add escapeCanonicalURL(). Due to some ridiculous accident of history, the other escapeFooURL() functions don't have a $variant parameter; I decided not to follow that bad example
* Reinstate the spirit of r44406 and r44412: instead of calling getInternalURL() (or getCanonicalURL()) and regexing the title parameter out, obtain the path to index.php using $wgCanonicalServer . $wgScript and append params to that. Sadly, we need to add a hook here to support the secure server hack for WMF, but that's the price of saner code in this case
* Introduce the {{canonicalurl:}} and {{canonicalurle:}} parser functions, which work just like {{fullurl:}} and {{fullurle:}} except that they use getCanonicalURL() instead of getFullURL()
* Use {{canonicalurl:}} in the enotif_body message, fixing bug 29993 (protocol-relative URLs appear in e-mail notifications)
2011-08-19 11:23:17 +00:00
*/
2011-12-12 19:19:23 +00:00
public function getCanonicalURL ( $query = '' , $query2 = false ) {
$query = self :: fixUrlQueryArgs ( $query , $query2 );
2011-12-12 20:03:50 +00:00
$url = wfExpandUrl ( $this -> getLocalURL ( $query ) . $this -> getFragmentForURL (), PROTO_CANONICAL );
2011-12-12 19:19:23 +00:00
wfRunHooks ( 'GetCanonicalURL' , array ( & $this , & $url , $query ) );
Per CR on r44412 and my promise in the commit summary of r94990, stop abusing $wgInternalServer (intended for Squid URLs) for IRC/e-mail URLs and introduce $wgCanonicalServer for these purposes instead. This revision introduces two new hooks for WMF hacks, in exchange for making the core code saner.
* Introduce $wgCanonicalServer, which should typically be a fully qualified version of $wgServer but in practice can be anything that you'd like to be used in IRC/e-mail notifs
** Default value is $wgServer, expanded to http:// if protocol-relative
** This means you can easily set HTTPS as the 'default' protocol to use in IRC and e-mail notifs by setting $wgCanonicalServer to https://example.com
* Introduce Title::getCanonicalURL(). Similar to getInternalURL(), including a hook for WMF usage (which will be needed as long as secure.wikimedia.org is used)
** Also add escapeCanonicalURL(). Due to some ridiculous accident of history, the other escapeFooURL() functions don't have a $variant parameter; I decided not to follow that bad example
* Reinstate the spirit of r44406 and r44412: instead of calling getInternalURL() (or getCanonicalURL()) and regexing the title parameter out, obtain the path to index.php using $wgCanonicalServer . $wgScript and append params to that. Sadly, we need to add a hook here to support the secure server hack for WMF, but that's the price of saner code in this case
* Introduce the {{canonicalurl:}} and {{canonicalurle:}} parser functions, which work just like {{fullurl:}} and {{fullurle:}} except that they use getCanonicalURL() instead of getFullURL()
* Use {{canonicalurl:}} in the enotif_body message, fixing bug 29993 (protocol-relative URLs appear in e-mail notifications)
2011-08-19 11:23:17 +00:00
return $url ;
}
2011-12-10 15:25:28 +00:00
/**
* HTML - escaped version of getCanonicalURL ()
2011-12-12 20:03:50 +00:00
*
* See getLocalURL for the arguments .
*
* @ see self :: getLocalURL
2011-12-10 15:25:28 +00:00
* @ since 1.18
2012-02-09 21:36:14 +00:00
* @ return string
2011-12-10 15:25:28 +00:00
*/
2011-12-12 19:19:23 +00:00
public function escapeCanonicalURL ( $query = '' , $query2 = false ) {
wfDeprecated ( __METHOD__ , '1.19' );
return htmlspecialchars ( $this -> getCanonicalURL ( $query , $query2 ) );
2011-12-10 15:25:28 +00:00
}
2004-09-30 05:21:20 +00:00
/**
* Get the edit URL for this Title
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return String the URL , or a null string if this is an
2009-12-14 20:37:51 +00:00
* interwiki link
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getEditURL () {
2010-07-25 15:53:22 +00:00
if ( $this -> mInterwiki != '' ) {
return '' ;
}
2004-08-16 20:14:35 +00:00
$s = $this -> getLocalURL ( 'action=edit' );
2003-04-14 23:10:40 +00:00
return $s ;
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
2008-03-02 13:57:56 +00:00
* Is $wgUser watching this page ?
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function userIsWatching () {
2003-04-14 23:10:40 +00:00
global $wgUser ;
2005-05-14 17:55:04 +00:00
if ( is_null ( $this -> mWatched ) ) {
2010-04-24 06:22:11 +00:00
if ( NS_SPECIAL == $this -> mNamespace || ! $wgUser -> isLoggedIn () ) {
2005-05-14 17:55:04 +00:00
$this -> mWatched = false ;
} else {
$this -> mWatched = $wgUser -> isWatched ( $this );
}
}
return $this -> mWatched ;
2003-04-14 23:10:40 +00:00
}
2011-11-06 19:59:46 +00:00
/**
* Can $wgUser read this page ?
*
* @ deprecated in 1.19 ; use userCan (), quickUserCan () or getUserPermissionsErrors () instead
* @ return Bool
* @ todo fold these checks into userCan ()
*/
public function userCanRead () {
2011-12-13 05:19:05 +00:00
wfDeprecated ( __METHOD__ , '1.19' );
2011-11-06 19:59:46 +00:00
return $this -> userCan ( 'read' );
}
2007-11-15 14:47:40 +00:00
/**
2011-12-11 16:04:53 +00:00
* Can $user perform $action on this page ?
2009-09-14 21:42:28 +00:00
* This skips potentially expensive cascading permission checks
* as well as avoids expensive error formatting
2007-01-13 03:22:20 +00:00
*
* Suitable for use for nonessential UI controls in common cases , but
* _not_ for functional access control .
*
* May provide false positives , but should never provide a false negative .
*
2011-01-05 23:08:13 +00:00
* @ param $action String action that permission needs to be checked for
2011-12-11 16:04:53 +00:00
* @ param $user User to check ( since 1.19 ); $wgUser will be used if not
* provided .
2011-01-05 23:08:13 +00:00
* @ return Bool
2009-12-14 20:37:51 +00:00
*/
2011-11-06 19:59:46 +00:00
public function quickUserCan ( $action , $user = null ) {
return $this -> userCan ( $action , $user , false );
2007-07-07 04:18:44 +00:00
}
2007-08-03 09:27:28 +00:00
2007-11-15 14:47:40 +00:00
/**
2011-12-11 16:04:53 +00:00
* Can $user perform $action on this page ?
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $action String action that permission needs to be checked for
2011-12-11 16:04:53 +00:00
* @ param $user User to check ( since 1.19 ); $wgUser will be used if not
* provided .
* @ param $doExpensiveQueries Bool Set this to false to avoid doing
* unnecessary queries .
2011-01-05 23:08:13 +00:00
* @ return Bool
2009-12-14 20:37:51 +00:00
*/
2011-11-06 19:59:46 +00:00
public function userCan ( $action , $user = null , $doExpensiveQueries = true ) {
if ( ! $user instanceof User ) {
global $wgUser ;
$user = $wgUser ;
}
return ! count ( $this -> getUserPermissionsErrorsInternal ( $action , $user , $doExpensiveQueries , true ) );
2007-08-01 10:19:26 +00:00
}
2007-11-15 14:47:40 +00:00
/**
2008-01-13 22:26:08 +00:00
* Can $user perform $action on this page ?
*
2011-05-17 22:03:20 +00:00
* @ todo FIXME : This * does not * check throttles ( User :: pingLimiter ()) .
2008-01-13 22:23:36 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $action String action that permission needs to be checked for
* @ param $user User to check
2011-12-11 16:04:53 +00:00
* @ param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary
* queries by skipping checks for cascading protections and user blocks .
* @ param $ignoreErrors Array of Strings Set this to a list of message keys
* whose corresponding errors may be ignored .
2011-02-02 11:00:09 +00:00
* @ return Array of arguments to wfMsg to explain permissions problems .
2007-10-11 07:11:51 +00:00
*/
2008-06-27 06:24:42 +00:00
public function getUserPermissionsErrors ( $action , $user , $doExpensiveQueries = true , $ignoreErrors = array () ) {
2010-04-24 06:22:11 +00:00
$errors = $this -> getUserPermissionsErrorsInternal ( $action , $user , $doExpensiveQueries );
2009-12-14 20:37:51 +00:00
2008-06-27 06:24:42 +00:00
// Remove the errors being ignored.
2010-04-24 06:22:11 +00:00
foreach ( $errors as $index => $error ) {
$error_key = is_array ( $error ) ? $error [ 0 ] : $error ;
2009-12-14 20:37:51 +00:00
2010-04-24 06:22:11 +00:00
if ( in_array ( $error_key , $ignoreErrors ) ) {
unset ( $errors [ $index ] );
2008-06-27 06:24:42 +00:00
}
}
2007-08-01 10:19:26 +00:00
return $errors ;
}
/**
2010-04-24 06:22:11 +00:00
* Permissions checks that fail most often , and which are easiest to test .
2008-01-13 22:23:36 +00:00
*
2010-04-24 06:22:11 +00:00
* @ param $action String the action to check
* @ param $user User user to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2007-08-01 10:19:26 +00:00
*/
2010-04-24 06:22:11 +00:00
private function checkQuickPermissions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
if ( $action == 'create' ) {
2011-12-12 06:03:01 +00:00
if ( ( $this -> isTalkPage () && ! $user -> isAllowed ( 'createtalk' ) ) ||
( ! $this -> isTalkPage () && ! $user -> isAllowed ( 'createpage' ) ) ) {
2010-07-25 15:53:22 +00:00
$errors [] = $user -> isAnon () ? array ( 'nocreatetext' ) : array ( 'nocreate-loggedin' );
2010-04-24 06:22:11 +00:00
}
} elseif ( $action == 'move' ) {
2011-12-12 06:03:01 +00:00
if ( ! $user -> isAllowed ( 'move-rootuserpages' )
&& $this -> mNamespace == NS_USER && ! $this -> isSubpage () ) {
2009-02-06 17:58:18 +00:00
// Show user page-specific message only if the user can move other pages
$errors [] = array ( 'cant-move-user-page' );
}
2009-12-14 20:37:51 +00:00
2009-02-06 17:58:18 +00:00
// Check if user is allowed to move files if it's a file
2011-12-12 06:03:01 +00:00
if ( $this -> mNamespace == NS_FILE && ! $user -> isAllowed ( 'movefile' ) ) {
2009-02-06 17:58:18 +00:00
$errors [] = array ( 'movenotallowedfile' );
}
2009-12-14 20:37:51 +00:00
2011-12-12 06:03:01 +00:00
if ( ! $user -> isAllowed ( 'move' ) ) {
2009-02-06 17:58:18 +00:00
// User can't move anything
2011-12-12 06:03:01 +00:00
global $wgGroupPermissions ;
$userCanMove = false ;
if ( isset ( $wgGroupPermissions [ 'user' ][ 'move' ] ) ) {
$userCanMove = $wgGroupPermissions [ 'user' ][ 'move' ];
}
$autoconfirmedCanMove = false ;
if ( isset ( $wgGroupPermissions [ 'autoconfirmed' ][ 'move' ] ) ) {
$autoconfirmedCanMove = $wgGroupPermissions [ 'autoconfirmed' ][ 'move' ];
}
2009-07-17 22:08:54 +00:00
if ( $user -> isAnon () && ( $userCanMove || $autoconfirmedCanMove ) ) {
2009-05-26 19:16:49 +00:00
// custom message if logged-in users without any special rights can move
2010-07-25 15:53:22 +00:00
$errors [] = array ( 'movenologintext' );
2009-05-26 19:16:49 +00:00
} else {
2010-07-25 15:53:22 +00:00
$errors [] = array ( 'movenotallowed' );
2009-05-26 19:16:49 +00:00
}
2009-02-06 17:58:18 +00:00
}
2010-04-24 06:22:11 +00:00
} elseif ( $action == 'move-target' ) {
2011-12-12 06:03:01 +00:00
if ( ! $user -> isAllowed ( 'move' ) ) {
2009-02-06 17:58:18 +00:00
// User can't move anything
2010-07-25 15:53:22 +00:00
$errors [] = array ( 'movenotallowed' );
2011-12-12 06:03:01 +00:00
} elseif ( ! $user -> isAllowed ( 'move-rootuserpages' )
&& $this -> mNamespace == NS_USER && ! $this -> isSubpage () ) {
2009-02-06 17:58:18 +00:00
// Show user page-specific message only if the user can move other pages
$errors [] = array ( 'cant-move-to-user-page' );
}
2011-12-12 06:03:01 +00:00
} elseif ( ! $user -> isAllowed ( $action ) ) {
2011-11-06 19:59:46 +00:00
$errors [] = $this -> missingPermissionError ( $action , $short );
2009-02-06 17:58:18 +00:00
}
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Add the resulting error code to the errors array
*
* @ param $errors Array list of current errors
* @ param $result Mixed result of errors
*
* @ return Array list of errors
*/
private function resultToError ( $errors , $result ) {
if ( is_array ( $result ) && count ( $result ) && ! is_array ( $result [ 0 ] ) ) {
// A single array representing an error
$errors [] = $result ;
2011-06-17 16:05:35 +00:00
} elseif ( is_array ( $result ) && is_array ( $result [ 0 ] ) ) {
2010-04-24 06:22:11 +00:00
// A nested array representing multiple errors
$errors = array_merge ( $errors , $result );
2011-06-17 16:05:35 +00:00
} elseif ( $result !== '' && is_string ( $result ) ) {
2010-04-24 06:22:11 +00:00
// A string representing a message-id
$errors [] = array ( $result );
2011-06-17 16:05:35 +00:00
} elseif ( $result === false ) {
2010-04-24 06:22:11 +00:00
// a generic "We don't want them to do that"
$errors [] = array ( 'badaccess-group0' );
2009-02-06 17:58:18 +00:00
}
2010-04-24 06:22:11 +00:00
return $errors ;
}
2009-02-06 17:58:18 +00:00
2010-04-24 06:22:11 +00:00
/**
* Check various permission hooks
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
* @ param $user User user to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkPermissionHooks ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2007-10-10 09:56:47 +00:00
// Use getUserPermissionsErrors instead
2010-09-02 22:08:09 +00:00
$result = '' ;
2010-04-24 06:22:11 +00:00
if ( ! wfRunHooks ( 'userCan' , array ( & $this , & $user , $action , & $result ) ) ) {
2007-08-01 10:19:26 +00:00
return $result ? array () : array ( array ( 'badaccess-group0' ) );
2006-02-05 18:47:40 +00:00
}
2009-01-29 21:09:14 +00:00
// Check getUserPermissionsErrors hook
2010-04-24 06:22:11 +00:00
if ( ! wfRunHooks ( 'getUserPermissionsErrors' , array ( & $this , & $user , $action , & $result ) ) ) {
2010-05-24 09:55:46 +00:00
$errors = $this -> resultToError ( $errors , $result );
2009-01-29 21:09:14 +00:00
}
// Check getUserPermissionsErrorsExpensive hook
2010-04-24 06:22:11 +00:00
if ( $doExpensiveQueries && ! ( $short && count ( $errors ) > 0 ) &&
! wfRunHooks ( 'getUserPermissionsErrorsExpensive' , array ( & $this , & $user , $action , & $result ) ) ) {
2010-05-24 09:55:46 +00:00
$errors = $this -> resultToError ( $errors , $result );
2009-01-29 21:09:14 +00:00
}
2009-12-14 20:37:51 +00:00
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Check permissions on special pages & namespaces
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
* @ param $user User user to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkSpecialsAndNSPermissions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2009-02-06 14:35:17 +00:00
# Only 'createaccount' and 'execute' can be performed on
# special pages, which don't actually exist in the DB.
2011-11-06 19:59:46 +00:00
$specialOKActions = array ( 'createaccount' , 'execute' , 'read' );
2010-04-24 06:22:11 +00:00
if ( NS_SPECIAL == $this -> mNamespace && ! in_array ( $action , $specialOKActions ) ) {
$errors [] = array ( 'ns-specialprotected' );
2004-10-27 07:37:35 +00:00
}
2008-04-14 07:45:50 +00:00
2009-02-06 14:35:17 +00:00
# Check $wgNamespaceProtection for restricted namespaces
2010-12-13 22:33:08 +00:00
if ( $this -> isNamespaceProtected ( $user ) ) {
2010-04-24 06:22:11 +00:00
$ns = $this -> mNamespace == NS_MAIN ?
2008-12-08 00:10:22 +00:00
wfMsg ( 'nstab-main' ) : $this -> getNsText ();
2010-04-24 06:22:11 +00:00
$errors [] = $this -> mNamespace == NS_MEDIAWIKI ?
array ( 'protectedinterface' ) : array ( 'namespaceprotected' , $ns );
2004-10-27 07:37:35 +00:00
}
2005-07-01 10:44:48 +00:00
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Check CSS / JS sub - page permissions
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
* @ param $user User user to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkCSSandJSPermissions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2009-02-06 14:35:17 +00:00
# Protect css/js subpages of user pages
2004-04-28 19:30:21 +00:00
# XXX: this might be better using restrictions
2009-08-02 08:31:29 +00:00
# XXX: right 'editusercssjs' is deprecated, for backward compatibility only
2010-04-24 06:22:11 +00:00
if ( $action != 'patrol' && ! $user -> isAllowed ( 'editusercssjs' )
&& ! preg_match ( '/^' . preg_quote ( $user -> getName (), '/' ) . '\//' , $this -> mTextform ) ) {
if ( $this -> isCssSubpage () && ! $user -> isAllowed ( 'editusercss' ) ) {
2011-06-05 18:29:43 +00:00
$errors [] = array ( 'customcssprotected' );
2011-06-17 16:05:35 +00:00
} elseif ( $this -> isJsSubpage () && ! $user -> isAllowed ( 'edituserjs' ) ) {
2011-06-05 18:29:43 +00:00
$errors [] = array ( 'customjsprotected' );
2010-04-24 06:22:11 +00:00
}
2004-10-27 07:37:35 +00:00
}
2008-04-14 07:45:50 +00:00
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Check against page_restrictions table requirements on this
* page . The user must possess all required rights for this
* action .
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
* @ param $user User user to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkPageRestrictions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
foreach ( $this -> getRestrictions ( $action ) as $right ) {
2009-02-06 14:35:17 +00:00
// Backwards compatibility, rewrite sysop -> protect
2010-04-24 06:22:11 +00:00
if ( $right == 'sysop' ) {
2009-02-06 14:35:17 +00:00
$right = 'protect' ;
}
2011-12-12 06:03:01 +00:00
if ( $right != '' && ! $user -> isAllowed ( $right ) ) {
2009-02-06 14:35:17 +00:00
// Users with 'editprotected' permission can edit protected pages
2011-11-06 19:59:46 +00:00
// without cascading option turned on.
2011-12-12 06:03:01 +00:00
if ( $action != 'edit' || ! $user -> isAllowed ( 'editprotected' )
2011-11-06 19:59:46 +00:00
|| $this -> mCascadeRestriction )
{
2009-02-06 14:35:17 +00:00
$errors [] = array ( 'protectedpagetext' , $right );
}
}
}
2009-12-14 20:37:51 +00:00
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Check restrictions on cascading pages .
2011-02-12 04:06:22 +00:00
*
2010-12-04 23:42:25 +00:00
* @ param $action String the action to check
2011-02-02 11:00:09 +00:00
* @ param $user User to check
2010-12-04 23:42:25 +00:00
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkCascadingSourcesRestrictions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
if ( $doExpensiveQueries && ! $this -> isCssJsSubpage () ) {
# We /could/ use the protection level on the source page, but it's
# fairly ugly as we have to establish a precedence hierarchy for pages
# included by multiple cascade-protected pages. So just restrict
# it to people with 'protect' permission, as they could remove the
# protection anyway.
2007-03-14 22:57:32 +00:00
list ( $cascadingSources , $restrictions ) = $this -> getCascadeProtectionSources ();
# Cascading protection depends on more than this page...
# Several cascading protected pages may include this page...
# Check each cascading level
# This is only for protection restrictions, not for all actions
2010-04-24 06:22:11 +00:00
if ( isset ( $restrictions [ $action ] ) ) {
foreach ( $restrictions [ $action ] as $right ) {
2007-03-14 22:57:32 +00:00
$right = ( $right == 'sysop' ) ? 'protect' : $right ;
2011-12-12 06:03:01 +00:00
if ( $right != '' && ! $user -> isAllowed ( $right ) ) {
2007-08-01 10:19:26 +00:00
$pages = '' ;
2010-04-24 06:22:11 +00:00
foreach ( $cascadingSources as $page )
2007-08-01 10:19:26 +00:00
$pages .= '* [[:' . $page -> getPrefixedText () . " ]] \n " ;
2007-08-09 22:13:44 +00:00
$errors [] = array ( 'cascadeprotected' , count ( $cascadingSources ), $pages );
2007-03-14 22:57:32 +00:00
}
}
2007-01-10 23:32:38 +00:00
}
}
2008-04-14 07:45:50 +00:00
2010-04-24 06:22:11 +00:00
return $errors ;
}
2007-12-01 09:08:43 +00:00
2010-04-24 06:22:11 +00:00
/**
* Check action permissions not already checked in checkQuickPermissions
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
2011-02-02 11:00:09 +00:00
* @ param $user User to check
2010-12-04 23:42:25 +00:00
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkActionPermissions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2011-12-27 15:29:44 +00:00
global $wgDeleteRevisionsLimit , $wgLang ;
2010-04-24 06:22:11 +00:00
if ( $action == 'protect' ) {
2011-12-10 15:44:44 +00:00
if ( count ( $this -> getUserPermissionsErrorsInternal ( 'edit' , $user , $doExpensiveQueries , true ) ) ) {
2010-04-24 06:22:11 +00:00
// If they can't edit, they shouldn't protect.
$errors [] = array ( 'protect-cantedit' );
}
} elseif ( $action == 'create' ) {
2007-12-11 09:51:56 +00:00
$title_protection = $this -> getTitleProtection ();
2010-04-23 19:36:37 +00:00
if ( $title_protection ) {
if ( $title_protection [ 'pt_create_perm' ] == 'sysop' ) {
$title_protection [ 'pt_create_perm' ] = 'protect' ; // B/C
2008-12-08 00:10:22 +00:00
}
2011-08-10 18:32:22 +00:00
if ( $title_protection [ 'pt_create_perm' ] == '' ||
2011-12-12 06:03:01 +00:00
! $user -> isAllowed ( $title_protection [ 'pt_create_perm' ] ) )
{
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'titleprotected' , User :: whoIs ( $title_protection [ 'pt_user' ] ), $title_protection [ 'pt_reason' ] );
2007-12-11 09:51:56 +00:00
}
}
2010-04-24 06:22:11 +00:00
} elseif ( $action == 'move' ) {
2008-10-11 08:26:01 +00:00
// Check for immobile pages
2010-04-24 06:22:11 +00:00
if ( ! MWNamespace :: isMovable ( $this -> mNamespace ) ) {
2008-10-11 08:26:01 +00:00
// Specific message for this case
$errors [] = array ( 'immobile-source-namespace' , $this -> getNsText () );
2010-04-24 06:22:11 +00:00
} elseif ( ! $this -> isMovable () ) {
2008-10-11 08:26:01 +00:00
// Less specific message for rarer cases
2011-08-30 11:39:43 +00:00
$errors [] = array ( 'immobile-source-page' );
2008-10-11 08:26:01 +00:00
}
2010-04-24 06:22:11 +00:00
} elseif ( $action == 'move-target' ) {
if ( ! MWNamespace :: isMovable ( $this -> mNamespace ) ) {
2008-10-11 08:26:01 +00:00
$errors [] = array ( 'immobile-target-namespace' , $this -> getNsText () );
2010-04-24 06:22:11 +00:00
} elseif ( ! $this -> isMovable () ) {
2008-10-11 08:26:01 +00:00
$errors [] = array ( 'immobile-target-page' );
}
2011-12-27 15:29:44 +00:00
} elseif ( $action == 'delete' ) {
if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
&& ! $this -> userCan ( 'bigdelete' , $user ) && $this -> isBigDeletion () )
{
$errors [] = array ( 'delete-toobig' , $wgLang -> formatNum ( $wgDeleteRevisionsLimit ) );
}
2007-07-11 08:43:21 +00:00
}
2010-04-24 06:22:11 +00:00
return $errors ;
}
/**
* Check that the user isn ' t blocked from editting .
2010-12-04 23:42:25 +00:00
*
* @ param $action String the action to check
2011-02-02 11:00:09 +00:00
* @ param $user User to check
2010-12-04 23:42:25 +00:00
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
2010-04-24 06:22:11 +00:00
*/
private function checkUserBlock ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2011-11-06 19:59:46 +00:00
// Account creation blocks handled at userlogin.
// Unblocking handled in SpecialUnblock
if ( ! $doExpensiveQueries || in_array ( $action , array ( 'createaccount' , 'unblock' ) ) ) {
2010-04-24 06:22:11 +00:00
return $errors ;
}
2010-07-25 15:53:22 +00:00
global $wgContLang , $wgLang , $wgEmailConfirmToEdit ;
2010-04-24 06:22:11 +00:00
2011-11-06 19:59:46 +00:00
if ( $wgEmailConfirmToEdit && ! $user -> isEmailConfirmed () ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'confirmedittext' );
}
2011-11-06 19:59:46 +00:00
if ( ( $action == 'edit' || $action == 'create' ) && ! $user -> isBlockedFrom ( $this ) ) {
2011-03-30 12:53:13 +00:00
// Don't block the user from editing their own talk page unless they've been
// explicitly blocked from that too.
2011-07-27 22:28:59 +00:00
} elseif ( $user -> isBlocked () && $user -> mBlock -> prevents ( $action ) !== false ) {
2012-02-16 00:54:34 +00:00
$block = $user -> getBlock ();
2011-07-27 22:28:59 +00:00
// This is from OutputPage::blockedPage
// Copied at r23888 by werdna
$id = $user -> blockedBy ();
$reason = $user -> blockedFor ();
if ( $reason == '' ) {
$reason = wfMsg ( 'blockednoreason' );
}
2011-08-18 20:03:30 +00:00
$ip = $user -> getRequest () -> getIP ();
2011-07-27 22:28:59 +00:00
if ( is_numeric ( $id ) ) {
$name = User :: whoIs ( $id );
} else {
$name = $id ;
}
$link = '[[' . $wgContLang -> getNsText ( NS_USER ) . " : { $name } | { $name } ]] " ;
$blockid = $block -> getId ();
2012-02-16 00:54:34 +00:00
$blockExpiry = $block -> getExpiry ();
$blockTimestamp = $wgLang -> timeanddate ( wfTimestamp ( TS_MW , $block -> mTimestamp ), true );
2011-07-27 22:28:59 +00:00
if ( $blockExpiry == 'infinity' ) {
$blockExpiry = wfMessage ( 'infiniteblock' ) -> text ();
} else {
$blockExpiry = $wgLang -> timeanddate ( wfTimestamp ( TS_MW , $blockExpiry ), true );
}
2012-02-16 00:54:34 +00:00
$intended = strval ( $block -> getTarget () );
2011-07-27 22:28:59 +00:00
$errors [] = array ( ( $block -> mAuto ? 'autoblockedtext' : 'blockedtext' ), $link , $reason , $ip , $name ,
$blockid , $blockExpiry , $intended , $blockTimestamp );
2010-04-24 06:22:11 +00:00
}
return $errors ;
}
2011-11-06 19:59:46 +00:00
/**
* Check that the user is allowed to read this page .
*
* @ param $action String the action to check
* @ param $user User to check
* @ param $errors Array list of current errors
* @ param $doExpensiveQueries Boolean whether or not to perform expensive queries
* @ param $short Boolean short circuit on first error
*
* @ return Array list of errors
*/
private function checkReadPermissions ( $action , $user , $errors , $doExpensiveQueries , $short ) {
2012-01-07 22:49:46 +00:00
global $wgWhitelistRead , $wgGroupPermissions , $wgRevokePermissions ;
2011-11-06 19:59:46 +00:00
static $useShortcut = null ;
# Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below
if ( is_null ( $useShortcut ) ) {
$useShortcut = true ;
if ( empty ( $wgGroupPermissions [ '*' ][ 'read' ] ) ) {
# Not a public wiki, so no shortcut
$useShortcut = false ;
} elseif ( ! empty ( $wgRevokePermissions ) ) {
/**
* Iterate through each group with permissions being revoked ( key not included since we don ' t care
* what the group name is ), then check if the read permission is being revoked . If it is , then
* we don ' t use the shortcut below since the user might not be able to read , even though anon
* reading is allowed .
*/
foreach ( $wgRevokePermissions as $perms ) {
if ( ! empty ( $perms [ 'read' ] ) ) {
# We might be removing the read right from the user, so no shortcut
$useShortcut = false ;
break ;
}
}
}
}
2012-01-05 20:29:53 +00:00
$whitelisted = false ;
if ( $useShortcut ) {
# Shortcut for public wikis, allows skipping quite a bit of code
$whitelisted = true ;
} elseif ( $user -> isAllowed ( 'read' ) ) {
# If the user is allowed to read pages, he is allowed to read all pages
$whitelisted = true ;
} elseif ( $this -> isSpecial ( 'Userlogin' )
2011-12-07 14:25:26 +00:00
|| $this -> isSpecial ( 'ChangePassword' )
|| $this -> isSpecial ( 'PasswordReset' )
) {
2012-01-05 20:29:53 +00:00
# Always grant access to the login page.
# Even anons need to be able to log in.
$whitelisted = true ;
} elseif ( is_array ( $wgWhitelistRead ) && count ( $wgWhitelistRead ) ) {
# Time to check the whitelist
# Only do these checks is there's something to check against
2011-11-06 19:59:46 +00:00
$name = $this -> getPrefixedText ();
$dbName = $this -> getPrefixedDBKey ();
2012-02-09 17:41:50 +00:00
// Check for explicit whitelisting with and without underscores
2011-11-06 19:59:46 +00:00
if ( in_array ( $name , $wgWhitelistRead , true ) || in_array ( $dbName , $wgWhitelistRead , true ) ) {
2012-01-05 20:29:53 +00:00
$whitelisted = true ;
} elseif ( $this -> getNamespace () == NS_MAIN ) {
# Old settings might have the title prefixed with
# a colon for main-namespace pages
2011-11-06 19:59:46 +00:00
if ( in_array ( ':' . $name , $wgWhitelistRead ) ) {
2012-01-05 20:29:53 +00:00
$whitelisted = true ;
2011-11-06 19:59:46 +00:00
}
2012-01-05 20:29:53 +00:00
} elseif ( $this -> isSpecialPage () ) {
# If it's a special page, ditch the subpage bit and check again
2011-11-06 19:59:46 +00:00
$name = $this -> getDBkey ();
list ( $name , /* $subpage */ ) = SpecialPageFactory :: resolveAlias ( $name );
if ( $name !== false ) {
$pure = SpecialPage :: getTitleFor ( $name ) -> getPrefixedText ();
if ( in_array ( $pure , $wgWhitelistRead , true ) ) {
2012-01-05 20:29:53 +00:00
$whitelisted = true ;
2011-11-06 19:59:46 +00:00
}
}
}
}
2012-01-06 06:16:05 +00:00
if ( ! $whitelisted ) {
# If the title is not whitelisted, give extensions a chance to do so...
wfRunHooks ( 'TitleReadWhitelist' , array ( $this , $user , & $whitelisted ) );
if ( ! $whitelisted ) {
$errors [] = $this -> missingPermissionError ( $action , $short );
}
2012-01-05 20:29:53 +00:00
}
2011-11-06 19:59:46 +00:00
return $errors ;
}
/**
* Get a description array when the user doesn ' t have the right to perform
* $action ( i . e . when User :: isAllowed () returns false )
*
* @ param $action String the action to check
* @ param $short Boolean short circuit on first error
* @ return Array list of errors
*/
private function missingPermissionError ( $action , $short ) {
// We avoid expensive display logic for quickUserCan's and such
if ( $short ) {
return array ( 'badaccess-group0' );
}
$groups = array_map ( array ( 'User' , 'makeGroupLinkWiki' ),
2011-12-12 06:03:01 +00:00
User :: getGroupsWithPermission ( $action ) );
2011-11-06 19:59:46 +00:00
if ( count ( $groups ) ) {
global $wgLang ;
return array (
'badaccess-groups' ,
$wgLang -> commaList ( $groups ),
count ( $groups )
);
} else {
return array ( 'badaccess-group0' );
}
}
2010-04-24 06:22:11 +00:00
/**
* Can $user perform $action on this page ? This is an internal function ,
* which checks ONLY that previously checked by userCan ( i . e . it leaves out
* checks on wfReadOnly () and blocks )
*
2011-01-05 23:08:13 +00:00
* @ param $action String action that permission needs to be checked for
* @ param $user User to check
* @ param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries .
* @ param $short Bool Set this to true to stop after the first permission error .
* @ return Array of arrays of the arguments to wfMsg to explain permissions problems .
2010-04-24 06:22:11 +00:00
*/
protected function getUserPermissionsErrorsInternal ( $action , $user , $doExpensiveQueries = true , $short = false ) {
wfProfileIn ( __METHOD__ );
2011-11-06 19:59:46 +00:00
# Read has special handling
if ( $action == 'read' ) {
$checks = array (
'checkPermissionHooks' ,
'checkReadPermissions' ,
);
} else {
$checks = array (
'checkQuickPermissions' ,
'checkPermissionHooks' ,
'checkSpecialsAndNSPermissions' ,
'checkCSSandJSPermissions' ,
'checkPageRestrictions' ,
'checkCascadingSourcesRestrictions' ,
'checkActionPermissions' ,
'checkUserBlock'
);
}
2010-04-24 06:22:11 +00:00
2011-12-11 14:48:45 +00:00
$errors = array ();
while ( count ( $checks ) > 0 &&
! ( $short && count ( $errors ) > 0 ) ) {
$method = array_shift ( $checks );
$errors = $this -> $method ( $action , $user , $errors , $doExpensiveQueries , $short );
}
wfProfileOut ( __METHOD__ );
return $errors ;
}
2012-02-12 19:25:28 +00:00
/**
* Protect css subpages of user pages : can $wgUser edit
* this page ?
*
* @ deprecated in 1.19 ; will be removed in 1.20 . Use getUserPermissionsErrors () instead .
* @ return Bool
*/
public function userCanEditCssSubpage () {
global $wgUser ;
wfDeprecated ( __METHOD__ , '1.19' );
return ( ( $wgUser -> isAllowedAll ( 'editusercssjs' , 'editusercss' ) )
|| preg_match ( '/^' . preg_quote ( $wgUser -> getName (), '/' ) . '\//' , $this -> mTextform ) );
}
/**
* Protect js subpages of user pages : can $wgUser edit
* this page ?
*
* @ deprecated in 1.19 ; will be removed in 1.20 . Use getUserPermissionsErrors () instead .
* @ return Bool
*/
public function userCanEditJsSubpage () {
global $wgUser ;
wfDeprecated ( __METHOD__ , '1.19' );
return ( ( $wgUser -> isAllowedAll ( 'editusercssjs' , 'edituserjs' ) )
|| preg_match ( '/^' . preg_quote ( $wgUser -> getName (), '/' ) . '\//' , $this -> mTextform ) );
}
2011-12-11 14:48:45 +00:00
/**
* Get a filtered list of all restriction types supported by this wiki .
* @ param bool $exists True to get all restriction types that apply to
* titles that do exist , False for all restriction types that apply to
* titles that do not exist
* @ return array
*/
public static function getFilteredRestrictionTypes ( $exists = true ) {
global $wgRestrictionTypes ;
$types = $wgRestrictionTypes ;
if ( $exists ) {
# Remove the create restriction for existing titles
$types = array_diff ( $types , array ( 'create' ) );
} else {
# Only the create and upload restrictions apply to non-existing titles
$types = array_intersect ( $types , array ( 'create' , 'upload' ) );
}
return $types ;
}
/**
* Returns restriction types for the current Title
*
* @ return array applicable restriction types
*/
public function getRestrictionTypes () {
if ( $this -> isSpecialPage () ) {
return array ();
}
$types = self :: getFilteredRestrictionTypes ( $this -> exists () );
if ( $this -> getNamespace () != NS_FILE ) {
# Remove the upload restriction for non-file titles
$types = array_diff ( $types , array ( 'upload' ) );
2010-04-24 06:22:11 +00:00
}
2007-07-11 08:43:21 +00:00
2011-12-11 14:48:45 +00:00
wfRunHooks ( 'TitleGetRestrictionTypes' , array ( $this , & $types ) );
wfDebug ( __METHOD__ . ': applicable restrictions to [[' .
$this -> getPrefixedText () . ']] are {' . implode ( ',' , $types ) . " } \n " );
return $types ;
2003-04-14 23:10:40 +00:00
}
2004-09-30 05:21:20 +00:00
2007-12-11 09:51:56 +00:00
/**
* Is this title subject to title protection ?
2011-01-06 21:26:20 +00:00
* Title protection is the one applied against creation of such title .
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Mixed An associative array representing any existent title
2007-12-16 19:57:40 +00:00
* protection , or false if there ' s none .
2007-12-11 09:51:56 +00:00
*/
2008-01-11 04:55:48 +00:00
private function getTitleProtection () {
2008-01-10 22:15:33 +00:00
// Can't protect pages in special namespaces
2008-01-11 05:06:10 +00:00
if ( $this -> getNamespace () < 0 ) {
2008-01-10 22:15:33 +00:00
return false ;
}
2009-12-14 20:37:51 +00:00
2009-08-06 16:26:04 +00:00
// Can't protect pages that exist.
2010-04-24 06:22:11 +00:00
if ( $this -> exists () ) {
2009-08-06 16:26:04 +00:00
return false ;
}
2008-01-10 22:15:33 +00:00
2010-04-24 00:23:57 +00:00
if ( ! isset ( $this -> mTitleProtection ) ) {
$dbr = wfGetDB ( DB_SLAVE );
$res = $dbr -> select ( 'protected_titles' , '*' ,
array ( 'pt_namespace' => $this -> getNamespace (), 'pt_title' => $this -> getDBkey () ),
__METHOD__ );
2007-12-11 09:51:56 +00:00
2010-04-24 00:23:57 +00:00
// fetchRow returns false if there are no rows.
$this -> mTitleProtection = $dbr -> fetchRow ( $res );
2007-12-11 09:51:56 +00:00
}
2010-04-24 00:23:57 +00:00
return $this -> mTitleProtection ;
2007-12-11 09:51:56 +00:00
}
2012-02-12 19:25:28 +00:00
/**
* Update the title protection status
*
* @ deprecated in 1.19 ; will be removed in 1.20 . Use WikiPage :: doUpdateRestrictions () instead .
* @ param $create_perm String Permission required for creation
* @ param $reason String Reason for protection
* @ param $expiry String Expiry timestamp
* @ return boolean true
*/
public function updateTitleProtection ( $create_perm , $reason , $expiry ) {
wfDeprecated ( __METHOD__ , '1.19' );
global $wgUser ;
$limit = array ( 'create' => $create_perm );
$expiry = array ( 'create' => $expiry );
$page = WikiPage :: factory ( $this );
$status = $page -> doUpdateRestrictions ( $limit , $expiry , false , $reason , $wgUser );
return $status -> isOK ();
}
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Remove any title protection due to page existing
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function deleteTitleProtection () {
$dbw = wfGetDB ( DB_MASTER );
$dbw -> delete (
'protected_titles' ,
array ( 'pt_namespace' => $this -> getNamespace (), 'pt_title' => $this -> getDBkey () ),
__METHOD__
);
$this -> mTitleProtection = false ;
2004-05-10 13:15:28 +00:00
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Is this page " semi-protected " - the * only * protection is autoconfirm ?
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param $action String Action to check ( default : edit )
2011-01-05 23:08:13 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isSemiProtected ( $action = 'edit' ) {
if ( $this -> exists () ) {
$restrictions = $this -> getRestrictions ( $action );
if ( count ( $restrictions ) > 0 ) {
foreach ( $restrictions as $restriction ) {
if ( strtolower ( $restriction ) != 'autoconfirmed' ) {
return false ;
}
}
} else {
# Not protected
return false ;
}
return true ;
} else {
# If it doesn't exist, it can't be protected
return false ;
}
2004-05-10 13:15:28 +00:00
}
2010-03-23 21:40:02 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Does the title correspond to a protected article ?
2009-08-01 16:44:44 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param $action String the action the page is protected from ,
* by default checks all actions .
2011-01-05 23:08:13 +00:00
* @ return Bool
2009-08-01 16:44:44 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isProtected ( $action = '' ) {
global $wgRestrictionLevels ;
$restrictionTypes = $this -> getRestrictionTypes ();
# Special pages have inherent protection
if ( $this -> isSpecialPage () ) {
return true ;
}
# Check regular protection levels
foreach ( $restrictionTypes as $type ) {
if ( $action == $type || $action == '' ) {
$r = $this -> getRestrictions ( $type );
foreach ( $wgRestrictionLevels as $level ) {
if ( in_array ( $level , $r ) && $level != '' ) {
return true ;
}
}
}
}
return false ;
2009-08-01 16:44:44 +00:00
}
2010-07-25 15:53:22 +00:00
2009-08-01 16:44:44 +00:00
/**
2011-12-11 14:48:45 +00:00
* Determines if $user is unable to edit this page because it has been protected
* by $wgNamespaceProtection .
2004-09-30 05:21:20 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param $user User object to check permissions
2011-01-05 23:08:13 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function isNamespaceProtected ( User $user ) {
global $wgNamespaceProtection ;
if ( isset ( $wgNamespaceProtection [ $this -> mNamespace ] ) ) {
foreach ( ( array ) $wgNamespaceProtection [ $this -> mNamespace ] as $right ) {
if ( $right != '' && ! $user -> isAllowed ( $right ) ) {
return true ;
}
}
}
return false ;
2004-05-10 13:15:28 +00:00
}
2007-01-12 07:31:34 +00:00
/**
* Cascading protection : Return true if cascading restrictions apply to this page , false if not .
*
2011-01-05 23:08:13 +00:00
* @ return Bool If the page is subject to cascading restrictions .
2007-01-12 07:31:34 +00:00
*/
2007-04-16 15:24:04 +00:00
public function isCascadeProtected () {
2007-08-21 03:57:54 +00:00
list ( $sources , /* $restrictions */ ) = $this -> getCascadeProtectionSources ( false );
2007-03-14 22:57:32 +00:00
return ( $sources > 0 );
2007-01-12 07:31:34 +00:00
}
2007-01-10 23:32:38 +00:00
/**
2007-01-12 04:43:33 +00:00
* Cascading protection : Get the source of any cascading restrictions on this page .
*
2011-01-05 23:08:13 +00:00
* @ param $getPages Bool Whether or not to retrieve the actual pages
2010-03-23 21:40:02 +00:00
* that the restrictions have come from .
2011-02-02 11:00:09 +00:00
* @ return Mixed Array of Title objects of the pages from which cascading restrictions
* have come , false for none , or true if such restrictions exist , but $getPages
* was not set . The restriction array is an array of each type , each of which
* contains a array of unique groups .
2007-01-12 04:43:33 +00:00
*/
2010-04-24 06:22:11 +00:00
public function getCascadeProtectionSources ( $getPages = true ) {
2011-03-18 19:15:56 +00:00
global $wgContLang ;
2007-03-14 22:57:32 +00:00
$pagerestrictions = array ();
2010-04-24 06:22:11 +00:00
if ( isset ( $this -> mCascadeSources ) && $getPages ) {
2007-03-14 22:57:32 +00:00
return array ( $this -> mCascadeSources , $this -> mCascadingRestrictions );
2011-06-17 16:05:35 +00:00
} elseif ( isset ( $this -> mHasCascadingRestrictions ) && ! $getPages ) {
2007-03-14 22:57:32 +00:00
return array ( $this -> mHasCascadingRestrictions , $pagerestrictions );
2007-01-12 04:43:33 +00:00
}
2007-01-12 08:37:07 +00:00
wfProfileIn ( __METHOD__ );
2008-11-06 22:30:55 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2007-01-12 04:43:33 +00:00
2008-12-01 17:14:30 +00:00
if ( $this -> getNamespace () == NS_FILE ) {
2010-07-25 15:53:22 +00:00
$tables = array ( 'imagelinks' , 'page_restrictions' );
2007-01-22 19:52:32 +00:00
$where_clauses = array (
'il_to' => $this -> getDBkey (),
'il_from=pr_page' ,
2010-07-25 15:53:22 +00:00
'pr_cascade' => 1
);
2007-01-12 04:43:33 +00:00
} else {
2010-07-25 15:53:22 +00:00
$tables = array ( 'templatelinks' , 'page_restrictions' );
2007-01-22 19:52:32 +00:00
$where_clauses = array (
'tl_namespace' => $this -> getNamespace (),
'tl_title' => $this -> getDBkey (),
'tl_from=pr_page' ,
2010-07-25 15:53:22 +00:00
'pr_cascade' => 1
);
2007-01-12 04:43:33 +00:00
}
2010-04-24 06:22:11 +00:00
if ( $getPages ) {
$cols = array ( 'pr_page' , 'page_namespace' , 'page_title' ,
'pr_expiry' , 'pr_type' , 'pr_level' );
2007-01-12 08:37:07 +00:00
$where_clauses [] = 'page_id=pr_page' ;
2007-01-12 09:10:30 +00:00
$tables [] = 'page' ;
2007-01-12 12:46:01 +00:00
} else {
2007-01-22 19:52:32 +00:00
$cols = array ( 'pr_expiry' );
2007-01-12 08:37:07 +00:00
}
2007-01-10 23:32:38 +00:00
2007-01-22 19:52:32 +00:00
$res = $dbr -> select ( $tables , $cols , $where_clauses , __METHOD__ );
2007-01-10 23:32:38 +00:00
2010-04-24 06:22:11 +00:00
$sources = $getPages ? array () : false ;
2007-01-22 19:52:32 +00:00
$now = wfTimestampNow ();
$purgeExpired = false ;
2008-04-14 07:45:50 +00:00
2010-04-24 06:22:11 +00:00
foreach ( $res as $row ) {
2011-03-18 19:15:56 +00:00
$expiry = $wgContLang -> formatExpiry ( $row -> pr_expiry , TS_MW );
2010-04-24 06:22:11 +00:00
if ( $expiry > $now ) {
if ( $getPages ) {
2007-01-12 07:31:34 +00:00
$page_id = $row -> pr_page ;
2007-01-12 08:37:07 +00:00
$page_ns = $row -> page_namespace ;
$page_title = $row -> page_title ;
2010-04-24 06:22:11 +00:00
$sources [ $page_id ] = Title :: makeTitle ( $page_ns , $page_title );
2007-03-14 22:57:32 +00:00
# Add groups needed for each restriction type if its not already there
# Make sure this restriction type still exists
2009-12-14 20:37:51 +00:00
2009-11-06 10:27:44 +00:00
if ( ! isset ( $pagerestrictions [ $row -> pr_type ] ) ) {
$pagerestrictions [ $row -> pr_type ] = array ();
}
2009-12-14 20:37:51 +00:00
2010-04-24 06:22:11 +00:00
if ( isset ( $pagerestrictions [ $row -> pr_type ] ) &&
! in_array ( $row -> pr_level , $pagerestrictions [ $row -> pr_type ] ) ) {
$pagerestrictions [ $row -> pr_type ][] = $row -> pr_level ;
2007-03-14 22:57:32 +00:00
}
2007-01-22 19:52:32 +00:00
} else {
$sources = true ;
2007-01-12 07:31:34 +00:00
}
2007-01-12 09:10:30 +00:00
} else {
2007-01-22 19:52:32 +00:00
// Trigger lazy purge of expired restrictions from the db
$purgeExpired = true ;
2007-01-12 09:10:30 +00:00
}
2007-01-22 19:52:32 +00:00
}
2010-04-24 06:22:11 +00:00
if ( $purgeExpired ) {
2007-01-22 19:52:32 +00:00
Title :: purgeExpiredRestrictions ();
2007-01-10 23:32:38 +00:00
}
2010-04-24 06:22:11 +00:00
if ( $getPages ) {
2007-01-12 08:37:07 +00:00
$this -> mCascadeSources = $sources ;
2007-03-14 22:57:32 +00:00
$this -> mCascadingRestrictions = $pagerestrictions ;
2007-01-12 09:10:30 +00:00
} else {
2007-01-12 08:37:07 +00:00
$this -> mHasCascadingRestrictions = $sources ;
}
2010-04-24 06:22:11 +00:00
2011-02-10 16:39:53 +00:00
wfProfileOut ( __METHOD__ );
2007-03-14 22:57:32 +00:00
return array ( $sources , $pagerestrictions );
2007-01-10 23:32:38 +00:00
}
2010-03-23 21:40:02 +00:00
2011-12-11 14:48:45 +00:00
/**
* Accessor / initialisation for mRestrictions
*
* @ param $action String action that permission needs to be checked for
* @ return Array of Strings the array of groups allowed to edit this article
*/
public function getRestrictions ( $action ) {
if ( ! $this -> mRestrictionsLoaded ) {
$this -> loadRestrictions ();
}
return isset ( $this -> mRestrictions [ $action ] )
? $this -> mRestrictions [ $action ]
: array ();
}
/**
* Get the expiry time for the restriction against a given action
*
* @ return String | Bool 14 - char timestamp , or 'infinity' if the page is protected forever
* or not protected at all , or false if the action is not recognised .
*/
public function getRestrictionExpiry ( $action ) {
if ( ! $this -> mRestrictionsLoaded ) {
$this -> loadRestrictions ();
}
return isset ( $this -> mRestrictionsExpiry [ $action ] ) ? $this -> mRestrictionsExpiry [ $action ] : false ;
}
2010-03-15 23:22:50 +00:00
/**
* Returns cascading restrictions for the current article
2010-03-23 21:40:02 +00:00
*
* @ return Boolean
2010-03-15 23:22:50 +00:00
*/
2007-01-11 00:31:04 +00:00
function areRestrictionsCascading () {
2010-04-24 06:22:11 +00:00
if ( ! $this -> mRestrictionsLoaded ) {
2007-01-10 23:32:38 +00:00
$this -> loadRestrictions ();
}
2007-01-11 00:31:04 +00:00
return $this -> mCascadeRestriction ;
2007-01-10 23:32:38 +00:00
}
2004-11-24 12:55:48 +00:00
/**
* Loads a string into mRestrictions array
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $res Resource restrictions as an SQL result .
2012-01-03 21:44:41 +00:00
* @ param $oldFashionedRestrictions String comma - separated list of page
* restrictions from page table ( pre 1.10 )
2004-11-24 12:55:48 +00:00
*/
2012-01-03 21:44:41 +00:00
private function loadRestrictionsFromResultWrapper ( $res , $oldFashionedRestrictions = null ) {
2009-08-06 16:26:04 +00:00
$rows = array ();
2009-12-14 20:37:51 +00:00
2010-10-13 23:11:40 +00:00
foreach ( $res as $row ) {
2009-08-06 16:26:04 +00:00
$rows [] = $row ;
}
2009-12-14 20:37:51 +00:00
2012-01-03 21:44:41 +00:00
$this -> loadRestrictionsFromRows ( $rows , $oldFashionedRestrictions );
2009-08-06 16:26:04 +00:00
}
2010-03-23 21:40:02 +00:00
2010-03-15 23:22:50 +00:00
/**
2010-03-23 21:40:02 +00:00
* Compiles list of active page restrictions from both page table ( pre 1.10 )
2011-01-06 22:19:42 +00:00
* and page_restrictions table for this existing page .
* Public for usage by LiquidThreads .
2010-03-23 21:40:02 +00:00
*
2010-03-15 23:22:50 +00:00
* @ param $rows array of db result objects
2012-01-03 21:44:41 +00:00
* @ param $oldFashionedRestrictions string comma - separated list of page
* restrictions from page table ( pre 1.10 )
2010-03-15 23:22:50 +00:00
*/
2012-01-03 21:44:41 +00:00
public function loadRestrictionsFromRows ( $rows , $oldFashionedRestrictions = null ) {
2011-03-18 19:15:56 +00:00
global $wgContLang ;
2008-03-04 21:27:23 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2009-12-14 20:37:51 +00:00
2009-11-09 12:05:30 +00:00
$restrictionTypes = $this -> getRestrictionTypes ();
2008-03-04 21:27:23 +00:00
2010-04-24 06:22:11 +00:00
foreach ( $restrictionTypes as $type ) {
2008-03-04 21:27:23 +00:00
$this -> mRestrictions [ $type ] = array ();
2011-03-18 19:15:56 +00:00
$this -> mRestrictionsExpiry [ $type ] = $wgContLang -> formatExpiry ( '' , TS_MW );
2008-03-04 21:27:23 +00:00
}
2007-01-10 23:32:38 +00:00
2008-02-26 20:41:21 +00:00
$this -> mCascadeRestriction = false ;
2007-01-10 23:32:38 +00:00
2007-01-12 01:44:33 +00:00
# Backwards-compatibility: also load the restrictions from the page record (old format).
2012-01-03 21:44:41 +00:00
if ( $oldFashionedRestrictions === null ) {
$oldFashionedRestrictions = $dbr -> selectField ( 'page' , 'page_restrictions' ,
2008-05-28 17:50:17 +00:00
array ( 'page_id' => $this -> getArticleId () ), __METHOD__ );
2007-01-12 01:44:33 +00:00
}
2012-01-03 21:44:41 +00:00
if ( $oldFashionedRestrictions != '' ) {
foreach ( explode ( ':' , trim ( $oldFashionedRestrictions ) ) as $restrict ) {
2007-01-12 01:44:33 +00:00
$temp = explode ( '=' , trim ( $restrict ) );
2010-04-24 06:22:11 +00:00
if ( count ( $temp ) == 1 ) {
2007-01-12 01:44:33 +00:00
// old old format should be treated as edit/move restriction
2008-03-04 21:27:23 +00:00
$this -> mRestrictions [ 'edit' ] = explode ( ',' , trim ( $temp [ 0 ] ) );
$this -> mRestrictions [ 'move' ] = explode ( ',' , trim ( $temp [ 0 ] ) );
2007-01-12 01:44:33 +00:00
} else {
$this -> mRestrictions [ $temp [ 0 ]] = explode ( ',' , trim ( $temp [ 1 ] ) );
}
}
2012-01-03 21:44:41 +00:00
$this -> mOldRestrictions = true ;
2007-01-12 01:44:33 +00:00
}
2007-01-10 23:32:38 +00:00
2010-04-24 06:22:11 +00:00
if ( count ( $rows ) ) {
2007-01-12 01:44:33 +00:00
# Current system - load second to make them override.
2007-01-22 19:52:32 +00:00
$now = wfTimestampNow ();
$purgeExpired = false ;
2007-01-22 08:26:41 +00:00
2011-02-02 11:00:09 +00:00
# Cycle through all the restrictions.
2010-04-24 06:22:11 +00:00
foreach ( $rows as $row ) {
2008-04-14 07:45:50 +00:00
2009-11-06 10:27:44 +00:00
// Don't take care of restrictions types that aren't allowed
2010-04-24 06:22:11 +00:00
if ( ! in_array ( $row -> pr_type , $restrictionTypes ) )
2008-03-04 21:27:23 +00:00
continue ;
2007-01-22 08:26:41 +00:00
// This code should be refactored, now that it's being used more generally,
// But I don't really see any harm in leaving it in Block for now -werdna
2011-03-18 19:15:56 +00:00
$expiry = $wgContLang -> formatExpiry ( $row -> pr_expiry , TS_MW );
2007-01-22 08:26:41 +00:00
// Only apply the restrictions if they haven't expired!
2007-01-22 19:52:32 +00:00
if ( ! $expiry || $expiry > $now ) {
2008-09-13 05:33:24 +00:00
$this -> mRestrictionsExpiry [ $row -> pr_type ] = $expiry ;
2007-01-22 08:26:41 +00:00
$this -> mRestrictions [ $row -> pr_type ] = explode ( ',' , trim ( $row -> pr_level ) );
2007-03-14 22:57:32 +00:00
2007-01-22 08:26:41 +00:00
$this -> mCascadeRestriction |= $row -> pr_cascade ;
2007-01-22 19:52:32 +00:00
} else {
// Trigger a lazy purge of expired restrictions
$purgeExpired = true ;
2007-01-22 08:26:41 +00:00
}
2007-01-12 01:44:33 +00:00
}
2007-04-16 15:24:04 +00:00
2010-04-24 06:22:11 +00:00
if ( $purgeExpired ) {
2007-01-22 19:52:32 +00:00
Title :: purgeExpiredRestrictions ();
}
2004-11-24 12:55:48 +00:00
}
2007-01-10 23:32:38 +00:00
2007-01-12 06:17:13 +00:00
$this -> mRestrictionsLoaded = true ;
2004-11-24 12:55:48 +00:00
}
2008-08-11 04:39:00 +00:00
/**
* Load restrictions from the page_restrictions table
2012-01-03 21:44:41 +00:00
*
* @ param $oldFashionedRestrictions String comma - separated list of page
* restrictions from page table ( pre 1.10 )
2008-08-11 04:39:00 +00:00
*/
2012-01-03 21:44:41 +00:00
public function loadRestrictions ( $oldFashionedRestrictions = null ) {
2011-03-18 19:15:56 +00:00
global $wgContLang ;
2010-04-24 06:22:11 +00:00
if ( ! $this -> mRestrictionsLoaded ) {
if ( $this -> exists () ) {
2007-12-11 09:51:56 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2011-03-18 19:15:56 +00:00
$res = $dbr -> select (
'page_restrictions' ,
'*' ,
array ( 'pr_page' => $this -> getArticleId () ),
__METHOD__
);
2007-12-11 09:51:56 +00:00
2012-01-03 21:44:41 +00:00
$this -> loadRestrictionsFromResultWrapper ( $res , $oldFashionedRestrictions );
2007-12-11 09:51:56 +00:00
} else {
$title_protection = $this -> getTitleProtection ();
2007-04-16 15:24:04 +00:00
2010-04-24 06:22:11 +00:00
if ( $title_protection ) {
2007-12-11 09:51:56 +00:00
$now = wfTimestampNow ();
2011-03-18 19:15:56 +00:00
$expiry = $wgContLang -> formatExpiry ( $title_protection [ 'pt_expiry' ], TS_MW );
2007-12-11 09:51:56 +00:00
2010-04-24 06:22:11 +00:00
if ( ! $expiry || $expiry > $now ) {
2007-12-11 09:51:56 +00:00
// Apply the restrictions
2008-09-13 05:33:24 +00:00
$this -> mRestrictionsExpiry [ 'create' ] = $expiry ;
2010-04-24 06:22:11 +00:00
$this -> mRestrictions [ 'create' ] = explode ( ',' , trim ( $title_protection [ 'pt_create_perm' ] ) );
2007-12-11 09:51:56 +00:00
} else { // Get rid of the old restrictions
Title :: purgeExpiredRestrictions ();
2011-01-06 21:26:20 +00:00
$this -> mTitleProtection = false ;
2007-12-11 09:51:56 +00:00
}
2008-07-03 12:25:52 +00:00
} else {
2011-03-18 19:15:56 +00:00
$this -> mRestrictionsExpiry [ 'create' ] = $wgContLang -> formatExpiry ( '' , TS_MW );
2007-12-11 09:51:56 +00:00
}
$this -> mRestrictionsLoaded = true ;
}
2007-01-10 23:32:38 +00:00
}
}
2011-12-18 16:01:31 +00:00
/**
* Flush the protection cache in this object and force reload from the database .
* This is used when updating protection from WikiPage :: doUpdateRestrictions () .
*/
public function flushRestrictions () {
$this -> mRestrictionsLoaded = false ;
$this -> mTitleProtection = null ;
}
2007-04-16 15:24:04 +00:00
/**
2007-01-22 08:26:41 +00:00
* Purge expired restrictions from the page_restrictions table
*/
static function purgeExpiredRestrictions () {
2007-01-22 18:59:08 +00:00
$dbw = wfGetDB ( DB_MASTER );
2010-07-25 15:53:22 +00:00
$dbw -> delete (
'page_restrictions' ,
2007-01-22 18:59:08 +00:00
array ( 'pr_expiry < ' . $dbw -> addQuotes ( $dbw -> timestamp () ) ),
2010-07-25 15:53:22 +00:00
__METHOD__
);
2007-12-11 09:51:56 +00:00
2010-07-25 15:53:22 +00:00
$dbw -> delete (
'protected_titles' ,
2007-12-11 09:51:56 +00:00
array ( 'pt_expiry < ' . $dbw -> addQuotes ( $dbw -> timestamp () ) ),
2010-07-25 15:53:22 +00:00
__METHOD__
);
2007-01-22 08:26:41 +00:00
}
2004-09-30 05:21:20 +00:00
/**
2011-12-11 14:48:45 +00:00
* Does this have subpages ? ( Warning , usually requires an extra DB query . )
2006-12-30 19:49:30 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function hasSubpages () {
if ( ! MWNamespace :: hasSubpages ( $this -> mNamespace ) ) {
# Duh
return false ;
2004-11-24 22:31:48 +00:00
}
2011-12-11 14:48:45 +00:00
# We dynamically add a member variable for the purpose of this method
# alone to cache the result. There's no point in having it hanging
# around uninitialized in every Title object; therefore we only add it
# if needed and don't declare it statically.
if ( isset ( $this -> mHasSubpages ) ) {
return $this -> mHasSubpages ;
}
$subpages = $this -> getSubpages ( 1 );
if ( $subpages instanceof TitleArray ) {
return $this -> mHasSubpages = ( bool ) $subpages -> count ();
}
return $this -> mHasSubpages = false ;
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2008-09-16 04:09:06 +00:00
/**
2011-12-11 14:48:45 +00:00
* Get all subpages of this page .
2010-03-23 21:40:02 +00:00
*
2011-12-11 14:48:45 +00:00
* @ param $limit Int maximum number of subpages to fetch ; - 1 for no limit
* @ return mixed TitleArray , or empty array if this page ' s namespace
* doesn ' t allow subpages
2008-09-16 04:09:06 +00:00
*/
2011-12-11 14:48:45 +00:00
public function getSubpages ( $limit = - 1 ) {
if ( ! MWNamespace :: hasSubpages ( $this -> getNamespace () ) ) {
return array ();
2008-09-16 04:09:06 +00:00
}
2011-12-11 14:48:45 +00:00
$dbr = wfGetDB ( DB_SLAVE );
$conds [ 'page_namespace' ] = $this -> getNamespace ();
$conds [] = 'page_title ' . $dbr -> buildLike ( $this -> getDBkey () . '/' , $dbr -> anyString () );
$options = array ();
if ( $limit > - 1 ) {
$options [ 'LIMIT' ] = $limit ;
}
return $this -> mSubpages = TitleArray :: newFromResult (
$dbr -> select ( 'page' ,
array ( 'page_id' , 'page_namespace' , 'page_title' , 'page_is_redirect' ),
$conds ,
__METHOD__ ,
$options
)
);
2008-09-16 04:09:06 +00:00
}
2004-09-30 05:21:20 +00:00
/**
* Is there a version of this page in the deletion archive ?
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Int the number of archived revisions
2004-09-30 05:21:20 +00:00
*/
2011-09-16 16:55:39 +00:00
public function isDeleted () {
2010-04-24 06:22:11 +00:00
if ( $this -> getNamespace () < 0 ) {
2008-08-10 20:34:46 +00:00
$n = 0 ;
} else {
$dbr = wfGetDB ( DB_SLAVE );
2011-07-06 16:47:29 +00:00
2009-12-14 20:37:51 +00:00
$n = $dbr -> selectField ( 'archive' , 'COUNT(*)' ,
2011-09-16 16:55:39 +00:00
array ( 'ar_namespace' => $this -> getNamespace (), 'ar_title' => $this -> getDBkey () ),
2009-02-24 16:17:23 +00:00
__METHOD__
);
2010-04-24 06:22:11 +00:00
if ( $this -> getNamespace () == NS_FILE ) {
2011-09-16 16:55:39 +00:00
$n += $dbr -> selectField ( 'filearchive' , 'COUNT(*)' ,
array ( 'fa_name' => $this -> getDBkey () ),
2009-02-24 16:17:23 +00:00
__METHOD__
);
2008-08-10 20:34:46 +00:00
}
2008-08-10 10:30:52 +00:00
}
2008-08-10 20:34:46 +00:00
return ( int ) $n ;
2003-08-31 22:21:50 +00:00
}
2009-12-14 20:37:51 +00:00
2009-02-24 16:17:23 +00:00
/**
* Is there a version of this page in the deletion archive ?
2010-03-23 21:40:02 +00:00
*
* @ return Boolean
2009-02-24 16:17:23 +00:00
*/
public function isDeletedQuick () {
2010-04-24 06:22:11 +00:00
if ( $this -> getNamespace () < 0 ) {
2009-02-24 16:17:23 +00:00
return false ;
}
$dbr = wfGetDB ( DB_SLAVE );
$deleted = ( bool ) $dbr -> selectField ( 'archive' , '1' ,
array ( 'ar_namespace' => $this -> getNamespace (), 'ar_title' => $this -> getDBkey () ),
__METHOD__
);
2010-04-24 06:22:11 +00:00
if ( ! $deleted && $this -> getNamespace () == NS_FILE ) {
2009-02-24 16:17:23 +00:00
$deleted = ( bool ) $dbr -> selectField ( 'filearchive' , '1' ,
array ( 'fa_name' => $this -> getDBkey () ),
__METHOD__
);
}
return $deleted ;
}
2003-04-14 23:10:40 +00:00
2012-01-01 12:44:28 +00:00
/**
2012-01-03 21:44:41 +00:00
* Get the article ID for this Title from the link cache ,
* adding it if necessary
2012-01-01 12:23:17 +00:00
*
2012-01-03 21:44:41 +00:00
* @ param $flags Int a bit field ; may be Title :: GAID_FOR_UPDATE to select
* for update
2011-01-05 23:08:13 +00:00
* @ return Int the ID
2004-09-30 05:21:20 +00:00
*/
2007-01-09 02:45:09 +00:00
public function getArticleID ( $flags = 0 ) {
2012-01-03 21:44:41 +00:00
if ( $this -> getNamespace () < 0 ) {
return $this -> mArticleID = 0 ;
}
$linkCache = LinkCache :: singleton ();
if ( $flags & self :: GAID_FOR_UPDATE ) {
$oldUpdate = $linkCache -> forUpdate ( true );
$linkCache -> clearLink ( $this );
$this -> mArticleID = $linkCache -> addLinkObj ( $this );
$linkCache -> forUpdate ( $oldUpdate );
} else {
if ( - 1 == $this -> mArticleID ) {
$this -> mArticleID = $linkCache -> addLinkObj ( $this );
}
2004-08-20 14:59:49 +00:00
}
2003-04-14 23:10:40 +00:00
return $this -> mArticleID ;
}
2008-04-14 07:45:50 +00:00
2008-04-09 05:21:00 +00:00
/**
* Is this an article that is a redirect page ?
2012-01-03 21:44:41 +00:00
* Uses link cache , adding it if necessary
2010-03-23 21:40:02 +00:00
*
2012-01-03 21:44:41 +00:00
* @ param $flags Int a bit field ; may be Title :: GAID_FOR_UPDATE to select for update
2011-01-05 23:08:13 +00:00
* @ return Bool
2008-04-09 05:21:00 +00:00
*/
public function isRedirect ( $flags = 0 ) {
2012-01-03 21:44:41 +00:00
if ( ! is_null ( $this -> mRedirect ) ) {
return $this -> mRedirect ;
2008-04-09 05:21:00 +00:00
}
2012-01-03 21:44:41 +00:00
# Calling getArticleID() loads the field from cache as needed
if ( ! $this -> getArticleID ( $flags ) ) {
return $this -> mRedirect = false ;
}
$linkCache = LinkCache :: singleton ();
$this -> mRedirect = ( bool ) $linkCache -> getGoodLinkFieldObj ( $this , 'redirect' );
2008-04-09 05:21:00 +00:00
2008-04-09 16:32:14 +00:00
return $this -> mRedirect ;
2008-04-09 05:21:00 +00:00
}
2008-04-14 07:45:50 +00:00
2008-04-09 05:21:00 +00:00
/**
* What is the length of this page ?
2012-01-03 21:44:41 +00:00
* Uses link cache , adding it if necessary
2010-03-23 21:40:02 +00:00
*
2012-01-03 21:44:41 +00:00
* @ param $flags Int a bit field ; may be Title :: GAID_FOR_UPDATE to select for update
2011-01-05 23:08:13 +00:00
* @ return Int
2008-04-09 05:21:00 +00:00
*/
public function getLength ( $flags = 0 ) {
2012-01-03 21:44:41 +00:00
if ( $this -> mLength != - 1 ) {
return $this -> mLength ;
2008-04-09 05:21:00 +00:00
}
2012-01-03 21:44:41 +00:00
# Calling getArticleID() loads the field from cache as needed
if ( ! $this -> getArticleID ( $flags ) ) {
return $this -> mLength = 0 ;
}
$linkCache = LinkCache :: singleton ();
$this -> mLength = intval ( $linkCache -> getGoodLinkFieldObj ( $this , 'length' ) );
2008-04-09 05:21:00 +00:00
2008-04-09 16:32:14 +00:00
return $this -> mLength ;
2008-04-09 05:21:00 +00:00
}
2003-04-14 23:10:40 +00:00
2008-05-06 14:54:17 +00:00
/**
2011-12-11 14:48:45 +00:00
* What is the page_latest field for this page ?
2004-09-30 05:21:20 +00:00
*
2012-01-03 21:44:41 +00:00
* @ param $flags Int a bit field ; may be Title :: GAID_FOR_UPDATE to select for update
2011-12-11 14:48:45 +00:00
* @ return Int or 0 if the page doesn ' t exist
2004-09-30 05:21:20 +00:00
*/
2011-12-11 14:48:45 +00:00
public function getLatestRevID ( $flags = 0 ) {
2012-01-03 21:44:41 +00:00
if ( $this -> mLatestID !== false ) {
return intval ( $this -> mLatestID );
2003-04-14 23:10:40 +00:00
}
2012-01-03 21:44:41 +00:00
# Calling getArticleID() loads the field from cache as needed
if ( ! $this -> getArticleID ( $flags ) ) {
return $this -> mLatestID = 0 ;
}
$linkCache = LinkCache :: singleton ();
$this -> mLatestID = intval ( $linkCache -> getGoodLinkFieldObj ( $this , 'revision' ) );
2011-12-11 14:48:45 +00:00
return $this -> mLatestID ;
2003-04-14 23:10:40 +00:00
}
2009-12-14 20:37:51 +00:00
2010-03-15 23:22:50 +00:00
/**
2011-12-11 14:48:45 +00:00
* This clears some fields in this object , and clears any associated
* keys in the " bad links " section of the link cache .
2010-03-23 21:40:02 +00:00
*
2011-12-27 21:22:47 +00:00
* - This is called from WikiPage :: doEdit () and WikiPage :: insertOn () to allow
2011-12-11 14:48:45 +00:00
* loading of the new page_id . It ' s also called from
2011-12-27 21:22:47 +00:00
* WikiPage :: doDeleteArticle ()
2011-12-11 14:48:45 +00:00
*
* @ param $newid Int the new Article ID
2010-03-15 23:22:50 +00:00
*/
2011-12-11 14:48:45 +00:00
public function resetArticleID ( $newid ) {
2012-01-03 21:44:41 +00:00
$linkCache = LinkCache :: singleton ();
$linkCache -> clearLink ( $this );
2009-12-14 20:37:51 +00:00
2012-01-03 21:44:41 +00:00
if ( $newid === false ) {
$this -> mArticleID = - 1 ;
2011-12-11 14:48:45 +00:00
} else {
2012-01-03 21:44:41 +00:00
$this -> mArticleID = intval ( $newid );
2011-12-11 14:48:45 +00:00
}
2012-01-03 21:44:41 +00:00
$this -> mRestrictionsLoaded = false ;
$this -> mRestrictions = array ();
$this -> mRedirect = null ;
$this -> mLength = - 1 ;
$this -> mLatestID = false ;
$this -> mEstimateRevisions = null ;
2009-09-30 10:36:22 +00:00
}
2009-12-14 20:37:51 +00:00
2009-10-09 12:52:16 +00:00
/**
2010-03-15 23:22:50 +00:00
* Capitalize a text string for a title if it belongs to a namespace that capitalizes
2010-03-23 21:40:02 +00:00
*
2011-02-02 11:00:09 +00:00
* @ param $text String containing title to capitalize
2010-03-23 21:40:02 +00:00
* @ param $ns int namespace index , defaults to NS_MAIN
* @ return String containing capitalized title
2009-10-09 12:52:16 +00:00
*/
public static function capitalize ( $text , $ns = NS_MAIN ) {
global $wgContLang ;
2009-12-14 20:37:51 +00:00
2010-07-25 15:53:22 +00:00
if ( MWNamespace :: isCapitalized ( $ns ) ) {
2009-10-09 12:52:16 +00:00
return $wgContLang -> ucfirst ( $text );
2010-07-25 15:53:22 +00:00
} else {
2009-10-09 12:52:16 +00:00
return $text ;
2010-07-25 15:53:22 +00:00
}
2009-10-09 12:52:16 +00:00
}
2009-09-30 10:36:22 +00:00
/**
* Secure and split - main initialisation function for this object
*
* Assumes that mDbkeyform has been set , and is urldecoded
* 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 .
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Bool true on success
2009-09-30 10:36:22 +00:00
*/
private function secureAndSplit () {
2009-10-09 12:52:16 +00:00
global $wgContLang , $wgLocalInterwiki ;
2009-09-30 10:36:22 +00:00
# Initialisation
2004-08-16 20:14:35 +00:00
$this -> mInterwiki = $this -> mFragment = '' ;
2004-03-20 15:03:26 +00:00
$this -> mNamespace = $this -> mDefaultNamespace ; # Usually NS_MAIN
2008-04-14 07:45:50 +00:00
2006-12-23 00:28:48 +00:00
$dbkey = $this -> mDbkeyform ;
2003-04-14 23:10:40 +00:00
2006-12-23 00:28:48 +00:00
# Strip Unicode bidi override characters.
# Sometimes they slip into cut-n-pasted page titles, where the
# override chars get included in list displays.
2008-11-27 20:11:38 +00:00
$dbkey = preg_replace ( '/\xE2\x80[\x8E\x8F\xAA-\xAE]/S' , '' , $dbkey );
2008-04-14 07:45:50 +00:00
2003-12-11 12:43:13 +00:00
# Clean up whitespace
2009-08-23 00:45:36 +00:00
# Note: use of the /u option on preg_replace here will cause
# input with invalid UTF-8 sequences to be nullified out in PHP 5.2.x,
# conveniently disabling them.
2009-09-25 15:15:33 +00:00
$dbkey = preg_replace ( '/[ _\xA0\x{1680}\x{180E}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}]+/u' , '_' , $dbkey );
2006-12-08 06:09:15 +00:00
$dbkey = trim ( $dbkey , '_' );
2004-04-09 00:45:15 +00:00
2010-01-06 19:59:42 +00:00
if ( $dbkey == '' ) {
2003-10-01 10:26:26 +00:00
return false ;
}
2005-07-01 10:44:48 +00:00
2010-04-24 06:22:11 +00:00
if ( false !== strpos ( $dbkey , UTF8_REPLACEMENT ) ) {
2004-09-03 06:12:57 +00:00
# Contained illegal UTF-8 sequences or forbidden Unicode chars.
return false ;
}
2003-04-14 23:10:40 +00:00
2006-12-08 06:09:15 +00:00
$this -> mDbkeyform = $dbkey ;
2004-03-06 01:49:16 +00:00
2005-08-20 15:30:22 +00:00
# Initial colon indicates main namespace rather than specified default
# but should not create invalid {ns,title} pairs such as {0,Project:Foo}
2011-06-19 21:05:26 +00:00
if ( ':' == $dbkey [ 0 ] ) {
2004-03-20 15:03:26 +00:00
$this -> mNamespace = NS_MAIN ;
2006-12-08 06:09:15 +00:00
$dbkey = substr ( $dbkey , 1 ); # remove the colon but continue processing
2007-05-04 14:30:02 +00:00
$dbkey = trim ( $dbkey , '_' ); # remove any subsequent whitespace
2005-08-20 15:30:22 +00:00
}
# Namespace or interwiki prefix
$firstPass = true ;
2009-01-15 18:36:59 +00:00
$prefixRegexp = " /^(.+?)_*:_*(.*) $ /S " ;
2005-08-20 15:30:22 +00:00
do {
2006-11-29 11:43:58 +00:00
$m = array ();
2009-01-15 18:36:59 +00:00
if ( preg_match ( $prefixRegexp , $dbkey , $m ) ) {
2005-08-20 15:30:22 +00:00
$p = $m [ 1 ];
2010-03-26 20:55:55 +00:00
if ( ( $ns = $wgContLang -> getNsIndex ( $p ) ) !== false ) {
2005-08-20 15:30:22 +00:00
# Ordinary namespace
2006-12-08 06:09:15 +00:00
$dbkey = $m [ 2 ];
2005-08-20 15:30:22 +00:00
$this -> mNamespace = $ns ;
2009-01-15 18:36:59 +00:00
# For Talk:X pages, check if X has a "namespace" prefix
2010-04-24 06:22:11 +00:00
if ( $ns == NS_TALK && preg_match ( $prefixRegexp , $dbkey , $x ) ) {
2010-07-25 15:53:22 +00:00
if ( $wgContLang -> getNsIndex ( $x [ 1 ] ) ) {
2011-02-02 11:00:09 +00:00
# Disallow Talk:File:x type titles...
return false ;
2011-06-17 16:05:35 +00:00
} elseif ( Interwiki :: isValidInterwiki ( $x [ 1 ] ) ) {
2011-02-02 11:00:09 +00:00
# Disallow Talk:Interwiki:x type titles...
return false ;
2010-07-25 15:53:22 +00:00
}
2009-01-15 18:36:59 +00:00
}
2010-04-24 06:22:11 +00:00
} elseif ( Interwiki :: isValidInterwiki ( $p ) ) {
if ( ! $firstPass ) {
2005-08-20 15:30:22 +00:00
# Can't make a local interwiki link to an interwiki link.
# That's just crazy!
return false ;
}
# Interwiki link
2006-12-08 06:09:15 +00:00
$dbkey = $m [ 2 ];
2006-10-13 23:32:36 +00:00
$this -> mInterwiki = $wgContLang -> lc ( $p );
2005-08-20 15:30:22 +00:00
# Redundant interwiki prefix to the local wiki
2010-12-06 15:00:56 +00:00
if ( $wgLocalInterwiki !== false
2011-02-12 04:06:22 +00:00
&& 0 == strcasecmp ( $this -> mInterwiki , $wgLocalInterwiki ) )
2010-12-06 15:00:56 +00:00
{
2010-04-24 06:22:11 +00:00
if ( $dbkey == '' ) {
2005-08-20 15:30:22 +00:00
# Can't have an empty self-link
2004-11-27 08:31:21 +00:00
return false ;
}
2005-08-20 15:30:22 +00:00
$this -> mInterwiki = '' ;
$firstPass = false ;
# Do another namespace split...
continue ;
2003-10-22 23:56:49 +00:00
}
2006-02-01 04:41:53 +00:00
2006-03-11 17:13:49 +00:00
# If there's an initial colon after the interwiki, that also
2006-02-01 04:41:53 +00:00
# resets the default namespace
2006-12-08 06:09:15 +00:00
if ( $dbkey !== '' && $dbkey [ 0 ] == ':' ) {
2006-02-01 04:41:53 +00:00
$this -> mNamespace = NS_MAIN ;
2006-12-08 06:09:15 +00:00
$dbkey = substr ( $dbkey , 1 );
2006-02-01 04:41:53 +00:00
}
2003-04-14 23:10:40 +00:00
}
2005-08-20 15:30:22 +00:00
# If there's no recognized interwiki or namespace,
# then let the colon expression be part of the title.
}
break ;
2010-04-24 06:22:11 +00:00
} while ( true );
2004-03-06 01:49:16 +00:00
2003-04-14 23:10:40 +00:00
# We already know that some pages won't be in the database!
2010-01-06 19:59:42 +00:00
if ( $this -> mInterwiki != '' || NS_SPECIAL == $this -> mNamespace ) {
2003-04-14 23:10:40 +00:00
$this -> mArticleID = 0 ;
}
2006-12-08 06:09:15 +00:00
$fragment = strstr ( $dbkey , '#' );
2007-03-27 21:43:21 +00:00
if ( false !== $fragment ) {
2011-04-05 22:38:37 +00:00
$this -> setFragment ( $fragment );
2006-12-08 06:09:15 +00:00
$dbkey = substr ( $dbkey , 0 , strlen ( $dbkey ) - strlen ( $fragment ) );
2004-09-14 05:35:34 +00:00
# remove whitespace again: prevents "Foo_bar_#"
# becoming "Foo_bar_"
2006-12-08 06:09:15 +00:00
$dbkey = preg_replace ( '/_*$/' , '' , $dbkey );
2003-04-14 23:10:40 +00:00
}
2003-12-11 18:39:03 +00:00
# Reject illegal characters.
2011-04-14 11:37:03 +00:00
$rxTc = self :: getTitleInvalidRegex ();
2010-04-24 06:22:11 +00:00
if ( preg_match ( $rxTc , $dbkey ) ) {
2003-12-11 18:39:03 +00:00
return false ;
}
2005-07-01 10:44:48 +00:00
2011-02-02 11:00:09 +00:00
# Pages with "/./" or "/../" appearing in the URLs will often be un-
# reachable due to the way web browsers deal with 'relative' URLs.
# Also, they conflict with subpage syntax. Forbid them explicitly.
2006-12-08 06:09:15 +00:00
if ( strpos ( $dbkey , '.' ) !== false &&
2011-02-12 04:06:22 +00:00
( $dbkey === '.' || $dbkey === '..' ||
strpos ( $dbkey , './' ) === 0 ||
strpos ( $dbkey , '../' ) === 0 ||
strpos ( $dbkey , '/./' ) !== false ||
strpos ( $dbkey , '/../' ) !== false ||
substr ( $dbkey , - 2 ) == '/.' ||
substr ( $dbkey , - 3 ) == '/..' ) )
2004-06-24 21:33:43 +00:00
{
2004-02-24 00:09:03 +00:00
return false ;
}
2008-04-14 07:45:50 +00:00
2011-02-02 11:00:09 +00:00
# Magic tilde sequences? Nu-uh!
2010-04-24 06:22:11 +00:00
if ( strpos ( $dbkey , '~~~' ) !== false ) {
2007-03-08 22:14:53 +00:00
return false ;
}
2003-12-11 18:39:03 +00:00
2011-02-02 11:00:09 +00:00
# Limit the size of titles to 255 bytes. This is typically the size of the
# underlying database field. We make an exception for special pages, which
# don't need to be stored in the database, and may edge over 255 bytes due
# to subpage syntax for long titles, e.g. [[Special:Block/Long name]]
2006-12-08 06:09:15 +00:00
if ( ( $this -> mNamespace != NS_SPECIAL && strlen ( $dbkey ) > 255 ) ||
2008-04-14 07:45:50 +00:00
strlen ( $dbkey ) > 512 )
2006-12-08 06:09:15 +00:00
{
2004-10-05 00:21:52 +00:00
return false ;
}
2011-02-02 11:00:09 +00:00
# Normally, all wiki links are forced to have an initial capital letter so [[foo]]
# and [[Foo]] point to the same place. Don't force it for interwikis, since the
# other site might be case-sensitive.
2007-06-03 10:44:27 +00:00
$this -> mUserCaseDBKey = $dbkey ;
2010-07-25 15:53:22 +00:00
if ( $this -> mInterwiki == '' ) {
2009-10-09 12:52:16 +00:00
$dbkey = self :: capitalize ( $dbkey , $this -> mNamespace );
2004-04-05 04:02:04 +00:00
}
2005-07-01 10:44:48 +00:00
2011-02-02 11:00:09 +00:00
# Can't make a link to a namespace alone... "empty" local links can only be
# self-links with a fragment identifier.
if ( $dbkey == '' && $this -> mInterwiki == '' && $this -> mNamespace != NS_MAIN ) {
2005-01-16 08:46:24 +00:00
return false ;
}
2011-02-02 11:00:09 +00:00
2007-09-04 04:13:53 +00:00
// Allow IPv6 usernames to start with '::' by canonicalizing IPv6 titles.
2008-04-14 07:45:50 +00:00
// IP names are not allowed for accounts, and can only be referring to
// edits from the IP. Given '::' abbreviations and caps/lowercaps,
// there are numerous ways to present the same IP. Having sp:contribs scan
// them all is silly and having some show the edits and others not is
2007-09-04 15:05:08 +00:00
// inconsistent. Same for talk/userpages. Keep them normalized instead.
2011-02-02 11:00:09 +00:00
$dbkey = ( $this -> mNamespace == NS_USER || $this -> mNamespace == NS_USER_TALK )
? IP :: sanitizeIP ( $dbkey )
: $dbkey ;
2006-04-30 05:04:38 +00:00
// Any remaining initial :s are illegal.
2011-09-23 20:20:41 +00:00
if ( $dbkey !== '' && ':' == $dbkey [ 0 ] ) {
2006-04-30 05:04:38 +00:00
return false ;
}
2008-04-14 07:45:50 +00:00
2004-03-06 01:49:16 +00:00
# Fill fields
2006-12-08 06:09:15 +00:00
$this -> mDbkeyform = $dbkey ;
$this -> mUrlform = wfUrlencode ( $dbkey );
2005-07-01 10:44:48 +00:00
2006-12-08 06:09:15 +00:00
$this -> mTextform = str_replace ( '_' , ' ' , $dbkey );
2008-03-03 19:54:22 +00:00
return true ;
2003-04-14 23:10:40 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get an array of Title objects linking to this Title
2005-05-04 00:33:08 +00:00
* Also stores the IDs in the link cache .
2004-09-30 05:21:20 +00:00
*
2006-06-18 12:42:16 +00:00
* WARNING : do not use this function on arbitrary user - supplied titles !
* On heavily - used templates it will max out the memory .
*
2010-03-23 21:40:02 +00:00
* @ param $options Array : may be FOR UPDATE
* @ param $table String : table name
* @ param $prefix String : fields prefix
2011-01-05 23:08:13 +00:00
* @ return Array of Title objects linking here
2004-09-30 05:21:20 +00:00
*/
2009-02-17 19:03:40 +00:00
public function getLinksTo ( $options = array (), $table = 'pagelinks' , $prefix = 'pl' ) {
if ( count ( $options ) > 0 ) {
2007-01-22 23:50:42 +00:00
$db = wfGetDB ( DB_MASTER );
2004-07-10 03:09:26 +00:00
} else {
2007-01-22 23:50:42 +00:00
$db = wfGetDB ( DB_SLAVE );
2004-07-10 03:09:26 +00:00
}
2005-07-01 10:44:48 +00:00
2010-07-25 15:53:22 +00:00
$res = $db -> select (
array ( 'page' , $table ),
2010-06-15 12:14:54 +00:00
array ( 'page_namespace' , 'page_title' , 'page_id' , 'page_len' , 'page_is_redirect' , 'page_latest' ),
2005-05-26 10:23:36 +00:00
array (
2005-12-30 09:33:11 +00:00
" { $prefix } _from=page_id " ,
" { $prefix } _namespace " => $this -> getNamespace (),
2008-01-14 09:26:36 +00:00
" { $prefix } _title " => $this -> getDBkey () ),
2008-07-27 18:59:46 +00:00
__METHOD__ ,
2010-07-25 15:53:22 +00:00
$options
);
2005-07-01 10:44:48 +00:00
2004-03-20 15:03:26 +00:00
$retVal = array ();
2011-12-29 15:12:00 +00:00
if ( $res -> numRows () ) {
$linkCache = LinkCache :: singleton ();
2010-04-24 06:22:11 +00:00
foreach ( $res as $row ) {
2010-11-01 00:10:00 +00:00
$titleObj = Title :: makeTitle ( $row -> page_namespace , $row -> page_title );
2010-11-01 00:07:17 +00:00
if ( $titleObj ) {
2011-09-20 15:19:18 +00:00
$linkCache -> addGoodLinkObjFromRow ( $titleObj , $row );
2004-05-30 22:38:13 +00:00
$retVal [] = $titleObj ;
}
2004-03-20 15:03:26 +00:00
}
}
return $retVal ;
}
2005-12-30 09:33:11 +00:00
/**
* Get an array of Title objects using this Title as a template
* Also stores the IDs in the link cache .
*
2006-06-18 12:42:16 +00:00
* WARNING : do not use this function on arbitrary user - supplied titles !
* On heavily - used templates it will max out the memory .
*
2010-03-23 21:40:02 +00:00
* @ param $options Array : may be FOR UPDATE
2011-01-05 23:08:13 +00:00
* @ return Array of Title the Title objects linking here
2005-12-30 09:33:11 +00:00
*/
2009-02-17 19:03:40 +00:00
public function getTemplateLinksTo ( $options = array () ) {
2005-12-30 09:33:11 +00:00
return $this -> getLinksTo ( $options , 'templatelinks' , 'tl' );
}
2011-12-29 15:12:00 +00:00
/**
* Get an array of Title objects linked from this Title
* Also stores the IDs in the link cache .
*
* WARNING : do not use this function on arbitrary user - supplied titles !
* On heavily - used templates it will max out the memory .
*
* @ param $options Array : may be FOR UPDATE
* @ param $table String : table name
* @ param $prefix String : fields prefix
* @ return Array of Title objects linking here
*/
public function getLinksFrom ( $options = array (), $table = 'pagelinks' , $prefix = 'pl' ) {
$id = $this -> getArticleId ();
# If the page doesn't exist; there can't be any link from this page
if ( ! $id ) {
return array ();
}
if ( count ( $options ) > 0 ) {
$db = wfGetDB ( DB_MASTER );
} else {
$db = wfGetDB ( DB_SLAVE );
}
$namespaceFiled = " { $prefix } _namespace " ;
$titleField = " { $prefix } _title " ;
$res = $db -> select (
array ( $table , 'page' ),
array ( $namespaceFiled , $titleField , 'page_id' , 'page_len' , 'page_is_redirect' , 'page_latest' ),
array ( " { $prefix } _from " => $id ),
__METHOD__ ,
$options ,
array ( 'page' => array ( 'LEFT JOIN' , array ( " page_namespace= $namespaceFiled " , " page_title= $titleField " ) ) )
);
$retVal = array ();
if ( $res -> numRows () ) {
$linkCache = LinkCache :: singleton ();
foreach ( $res as $row ) {
$titleObj = Title :: makeTitle ( $row -> $namespaceFiled , $row -> $titleField );
if ( $titleObj ) {
if ( $row -> page_id ) {
$linkCache -> addGoodLinkObjFromRow ( $titleObj , $row );
} else {
$linkCache -> addBadLinkObj ( $titleObj );
}
$retVal [] = $titleObj ;
}
}
}
return $retVal ;
}
/**
* Get an array of Title objects used on this Title as a template
* Also stores the IDs in the link cache .
*
* WARNING : do not use this function on arbitrary user - supplied titles !
* On heavily - used templates it will max out the memory .
*
* @ param $options Array : may be FOR UPDATE
* @ return Array of Title the Title objects used here
*/
public function getTemplateLinksFrom ( $options = array () ) {
return $this -> getLinksFrom ( $options , 'templatelinks' , 'tl' );
}
2005-04-24 04:13:47 +00:00
/**
* Get an array of Title objects referring to non - existent articles linked from this page
*
2007-09-14 04:21:36 +00:00
* @ todo check if needed ( used only in SpecialBrokenRedirects . php , and should use redirect table in this case )
2011-01-05 23:08:13 +00:00
* @ return Array of Title the Title objects
2005-04-24 04:13:47 +00:00
*/
2009-02-18 08:35:15 +00:00
public function getBrokenLinksFrom () {
2007-09-14 04:21:36 +00:00
if ( $this -> getArticleId () == 0 ) {
# All links from article ID 0 are false positives
return array ();
}
2009-02-18 08:35:15 +00:00
$dbr = wfGetDB ( DB_SLAVE );
$res = $dbr -> select (
array ( 'page' , 'pagelinks' ),
array ( 'pl_namespace' , 'pl_title' ),
array (
'pl_from' => $this -> getArticleId (),
'page_namespace IS NULL'
),
__METHOD__ , array (),
array (
2009-12-14 20:37:51 +00:00
'page' => array (
'LEFT JOIN' ,
2009-02-18 08:35:15 +00:00
array ( 'pl_namespace=page_namespace' , 'pl_title=page_title' )
)
)
);
2005-07-01 10:44:48 +00:00
2005-04-24 04:13:47 +00:00
$retVal = array ();
2010-04-24 06:22:11 +00:00
foreach ( $res as $row ) {
2009-02-18 08:35:15 +00:00
$retVal [] = Title :: makeTitle ( $row -> pl_namespace , $row -> pl_title );
2005-04-24 04:13:47 +00:00
}
return $retVal ;
}
2004-09-30 05:21:20 +00:00
/**
* Get a list of URLs to purge from the Squid cache when this
* page changes
*
2011-01-05 23:08:13 +00:00
* @ return Array of String the URLs
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getSquidURLs () {
2006-10-12 10:34:49 +00:00
global $wgContLang ;
$urls = array (
2004-03-20 15:03:26 +00:00
$this -> getInternalURL (),
2004-08-16 20:14:35 +00:00
$this -> getInternalURL ( 'action=history' )
2004-03-20 15:03:26 +00:00
);
2006-10-12 10:34:49 +00:00
// purge variant urls as well
2010-04-24 06:22:11 +00:00
if ( $wgContLang -> hasVariants () ) {
2006-10-12 10:34:49 +00:00
$variants = $wgContLang -> getVariants ();
2010-04-24 06:22:11 +00:00
foreach ( $variants as $vCode ) {
$urls [] = $this -> getInternalURL ( '' , $vCode );
2006-10-12 10:34:49 +00:00
}
}
return $urls ;
2004-03-20 15:03:26 +00:00
}
2004-03-23 10:22:49 +00:00
2008-08-11 04:39:00 +00:00
/**
* Purge all applicable Squid URLs
*/
2007-04-16 15:24:04 +00:00
public function purgeSquid () {
2006-06-18 12:42:16 +00:00
global $wgUseSquid ;
if ( $wgUseSquid ) {
$urls = $this -> getSquidURLs ();
$u = new SquidUpdate ( $urls );
$u -> doUpdate ();
}
}
2004-09-30 05:21:20 +00:00
/**
* Move this page without authentication
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $nt Title the new page Title
* @ return Mixed true on success , getUserPermissionsErrors () - like array on failure
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function moveNoAuth ( & $nt ) {
2004-03-23 10:22:49 +00:00
return $this -> moveTo ( $nt , false );
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
2005-04-25 09:25:06 +00:00
* Check whether a given move operation would be valid .
2008-05-27 14:42:51 +00:00
* Returns true if ok , or a getUserPermissionsErrors () - like array otherwise
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $nt Title the new title
* @ param $auth Bool indicates whether $wgUser ' s permissions
2009-12-14 20:37:51 +00:00
* should be checked
2011-01-05 23:08:13 +00:00
* @ param $reason String is the log summary of the move , used for spam checking
* @ return Mixed True on success , getUserPermissionsErrors () - like array on failure
2004-09-30 05:21:20 +00:00
*/
2008-06-24 13:03:16 +00:00
public function isValidMoveOperation ( & $nt , $auth = true , $reason = '' ) {
2008-10-11 08:26:01 +00:00
global $wgUser ;
2009-12-14 20:37:51 +00:00
$errors = array ();
2010-04-24 06:22:11 +00:00
if ( ! $nt ) {
2008-05-27 16:03:21 +00:00
// Normally we'd add this to $errors, but we'll get
// lots of syntax errors if $nt is not an object
2010-04-24 06:22:11 +00:00
return array ( array ( 'badtitletext' ) );
2004-03-23 10:22:49 +00:00
}
2010-04-24 06:22:11 +00:00
if ( $this -> equals ( $nt ) ) {
$errors [] = array ( 'selfmove' );
2005-04-25 09:25:06 +00:00
}
2010-04-24 06:22:11 +00:00
if ( ! $this -> isMovable () ) {
2008-10-11 08:26:01 +00:00
$errors [] = array ( 'immobile-source-namespace' , $this -> getNsText () );
}
2008-12-15 14:48:46 +00:00
if ( $nt -> getInterwiki () != '' ) {
2008-12-15 15:47:42 +00:00
$errors [] = array ( 'immobile-target-namespace-iw' );
2008-12-15 14:48:46 +00:00
}
2008-10-11 08:26:01 +00:00
if ( ! $nt -> isMovable () ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'immobile-target-namespace' , $nt -> getNsText () );
2005-04-25 09:25:06 +00:00
}
2004-03-23 10:22:49 +00:00
2004-04-29 02:14:54 +00:00
$oldid = $this -> getArticleID ();
$newid = $nt -> getArticleID ();
2004-03-23 10:22:49 +00:00
if ( strlen ( $nt -> getDBkey () ) < 1 ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'articleexists' );
2004-03-23 10:22:49 +00:00
}
2010-01-06 19:59:42 +00:00
if ( ( $this -> getDBkey () == '' ) ||
2004-03-23 10:22:49 +00:00
( ! $oldid ) ||
2011-02-12 04:06:22 +00:00
( $nt -> getDBkey () == '' ) ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'badarticleerror' );
2004-03-23 10:22:49 +00:00
}
2008-05-03 13:09:34 +00:00
// Image-specific checks
2011-03-05 14:47:11 +00:00
if ( $this -> getNamespace () == NS_FILE ) {
$errors = array_merge ( $errors , $this -> validateFileMoveOperation ( $nt ) );
}
2011-03-19 04:24:15 +00:00
2011-03-05 14:47:11 +00:00
if ( $nt -> getNamespace () == NS_FILE && $this -> getNamespace () != NS_FILE ) {
$errors [] = array ( 'nonfile-cannot-move-to-file' );
}
2008-05-03 13:09:34 +00:00
2008-01-18 15:52:40 +00:00
if ( $auth ) {
2008-11-01 23:20:25 +00:00
$errors = wfMergeErrorArrays ( $errors ,
2010-04-24 06:22:11 +00:00
$this -> getUserPermissionsErrors ( 'move' , $wgUser ),
$this -> getUserPermissionsErrors ( 'edit' , $wgUser ),
$nt -> getUserPermissionsErrors ( 'move-target' , $wgUser ),
$nt -> getUserPermissionsErrors ( 'edit' , $wgUser ) );
2004-03-23 10:22:49 +00:00
}
2005-07-01 10:44:48 +00:00
2009-03-19 21:48:16 +00:00
$match = EditPage :: matchSummarySpamRegex ( $reason );
2010-04-24 06:22:11 +00:00
if ( $match !== false ) {
2008-08-19 20:32:30 +00:00
// This is kind of lame, won't display nice
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'spamprotectiontext' );
2008-08-19 20:32:30 +00:00
}
2009-12-14 20:37:51 +00:00
2007-12-08 18:30:00 +00:00
$err = null ;
2010-04-24 06:22:11 +00:00
if ( ! wfRunHooks ( 'AbortMove' , array ( $this , $nt , $wgUser , & $err , $reason ) ) ) {
$errors [] = array ( 'hookaborted' , $err );
2007-12-08 18:30:00 +00:00
}
2004-03-23 10:22:49 +00:00
# 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
2010-07-23 17:11:20 +00:00
if ( ! $this -> isValidMoveTarget ( $nt ) ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'articleexists' );
2004-03-23 10:22:49 +00:00
}
2007-12-15 23:51:54 +00:00
} else {
$tp = $nt -> getTitleProtection ();
2008-05-12 14:12:51 +00:00
$right = ( $tp [ 'pt_create_perm' ] == 'sysop' ) ? 'protect' : $tp [ 'pt_create_perm' ];
if ( $tp and ! $wgUser -> isAllowed ( $right ) ) {
2010-04-24 06:22:11 +00:00
$errors [] = array ( 'cantmove-titleprotected' );
2007-12-15 23:51:54 +00:00
}
2005-04-25 09:25:06 +00:00
}
2010-07-25 15:53:22 +00:00
if ( empty ( $errors ) ) {
2008-05-27 16:03:21 +00:00
return true ;
2010-07-25 15:53:22 +00:00
}
2008-05-27 16:03:21 +00:00
return $errors ;
2005-04-25 09:25:06 +00:00
}
2011-03-19 04:24:15 +00:00
2011-03-05 14:44:28 +00:00
/**
* Check if the requested move target is a valid file move target
* @ param Title $nt Target title
* @ return array List of errors
*/
protected function validateFileMoveOperation ( $nt ) {
global $wgUser ;
2011-03-19 04:24:15 +00:00
2011-03-05 14:44:28 +00:00
$errors = array ();
2011-03-19 04:24:15 +00:00
2011-09-29 18:35:34 +00:00
// wfFindFile( $nt ) / wfLocalFile( $nt ) is not allowed until below
2011-09-29 22:11:51 +00:00
2011-03-05 14:44:28 +00:00
$file = wfLocalFile ( $this );
if ( $file -> exists () ) {
if ( $nt -> getText () != wfStripIllegalFilenameChars ( $nt -> getText () ) ) {
$errors [] = array ( 'imageinvalidfilename' );
}
if ( ! File :: checkExtensionCompatibility ( $file , $nt -> getDBkey () ) ) {
$errors [] = array ( 'imagetypemismatch' );
}
}
2011-09-29 22:11:51 +00:00
2011-09-29 18:35:34 +00:00
if ( $nt -> getNamespace () != NS_FILE ) {
$errors [] = array ( 'imagenocrossnamespace' );
2011-09-29 22:11:51 +00:00
// From here we want to do checks on a file object, so if we can't
2011-09-29 18:35:34 +00:00
// create one, we must return.
return $errors ;
}
2011-09-29 22:11:51 +00:00
2011-09-29 18:35:34 +00:00
// wfFindFile( $nt ) / wfLocalFile( $nt ) is allowed below here
2011-03-19 04:24:15 +00:00
2011-03-05 14:44:28 +00:00
$destFile = wfLocalFile ( $nt );
2011-03-21 21:26:16 +00:00
if ( ! $wgUser -> isAllowed ( 'reupload-shared' ) && ! $destFile -> exists () && wfFindFile ( $nt ) ) {
2011-03-05 14:44:28 +00:00
$errors [] = array ( 'file-exists-sharedrepo' );
}
2011-03-19 04:24:15 +00:00
2011-03-05 14:44:28 +00:00
return $errors ;
}
2005-07-01 10:44:48 +00:00
2005-04-25 09:25:06 +00:00
/**
* Move a title to a new location
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $nt Title the new title
* @ param $auth Bool indicates whether $wgUser ' s permissions
2009-12-14 20:37:51 +00:00
* should be checked
2011-02-02 11:00:09 +00:00
* @ param $reason String the reason for the move
2011-01-05 23:08:13 +00:00
* @ param $createRedirect Bool Whether to create a redirect from the old title to the new title .
2007-11-23 11:27:09 +00:00
* Ignored if the user doesn ' t have the suppressredirect right .
2011-01-05 23:08:13 +00:00
* @ return Mixed true on success , getUserPermissionsErrors () - like array on failure
2005-04-25 09:25:06 +00:00
*/
2007-11-06 16:14:24 +00:00
public function moveTo ( & $nt , $auth = true , $reason = '' , $createRedirect = true ) {
2011-10-09 12:30:13 +00:00
global $wgUser ;
2008-06-24 13:03:16 +00:00
$err = $this -> isValidMoveOperation ( $nt , $auth , $reason );
2010-04-24 06:22:11 +00:00
if ( is_array ( $err ) ) {
2011-10-09 12:30:13 +00:00
// Auto-block user's IP if the account was "hard" blocked
$wgUser -> spreadAnyEditBlock ();
2005-04-25 09:25:06 +00:00
return $err ;
}
2005-07-01 10:44:48 +00:00
2011-09-21 21:32:24 +00:00
// If it is a file, move it first.
// It is done before all other moving stuff is done because it's hard to revert.
2009-03-29 13:42:29 +00:00
$dbw = wfGetDB ( DB_MASTER );
2010-04-24 06:22:11 +00:00
if ( $this -> getNamespace () == NS_FILE ) {
2009-03-29 13:42:29 +00:00
$file = wfLocalFile ( $this );
2010-04-24 06:22:11 +00:00
if ( $file -> exists () ) {
2009-03-29 13:42:29 +00:00
$status = $file -> move ( $nt );
2010-04-24 06:22:11 +00:00
if ( ! $status -> isOk () ) {
2009-03-29 13:42:29 +00:00
return $status -> getErrorsArray ();
}
}
2011-12-30 15:54:11 +00:00
// Clear RepoGroup process cache
RepoGroup :: singleton () -> clearCache ( $this );
RepoGroup :: singleton () -> clearCache ( $nt ); # clear false negative cache
2009-03-29 13:42:29 +00:00
}
2011-03-21 16:46:17 +00:00
$dbw -> begin (); # If $file was a LocalFile, its transaction would have closed our own.
2011-05-07 09:55:18 +00:00
$pageid = $this -> getArticleID ( self :: GAID_FOR_UPDATE );
2008-09-24 15:02:02 +00:00
$protected = $this -> isProtected ();
2008-05-30 19:59:47 +00:00
2011-01-03 20:32:04 +00:00
// Do the actual move
2011-09-29 22:08:00 +00:00
$err = $this -> moveToInternal ( $nt , $reason , $createRedirect );
2010-04-24 06:22:11 +00:00
if ( is_array ( $err ) ) {
2011-05-17 22:03:20 +00:00
# @todo FIXME: What about the File we have already moved?
2011-03-21 16:46:17 +00:00
$dbw -> rollback ();
2008-05-03 13:09:34 +00:00
return $err ;
}
2011-01-03 20:32:04 +00:00
2010-08-03 20:50:38 +00:00
// Refresh the sortkey for this row. Be careful to avoid resetting
// cl_timestamp, which may disturb time-based lists on some sites.
2011-03-14 02:19:28 +00:00
$prefixes = $dbw -> select (
2010-08-03 20:50:38 +00:00
'categorylinks' ,
2011-03-14 02:19:28 +00:00
array ( 'cl_sortkey_prefix' , 'cl_to' ),
2010-08-03 20:50:38 +00:00
array ( 'cl_from' => $pageid ),
__METHOD__
);
2011-03-14 02:19:28 +00:00
foreach ( $prefixes as $prefixRow ) {
$prefix = $prefixRow -> cl_sortkey_prefix ;
$catTo = $prefixRow -> cl_to ;
$dbw -> update ( 'categorylinks' ,
array (
'cl_sortkey' => Collation :: singleton () -> getSortKey (
$nt -> getCategorySortkey ( $prefix ) ),
'cl_timestamp=cl_timestamp' ),
array (
'cl_from' => $pageid ,
'cl_to' => $catTo ),
__METHOD__
);
}
2004-08-06 16:25:27 +00:00
2011-12-30 15:54:11 +00:00
$redirid = $this -> getArticleID ();
2010-04-24 06:22:11 +00:00
if ( $protected ) {
2008-09-24 15:02:02 +00:00
# Protect the redirect title as the title used to be...
$dbw -> insertSelect ( 'page_restrictions' , 'page_restrictions' ,
2009-12-14 20:37:51 +00:00
array (
2008-09-24 15:02:02 +00:00
'pr_page' => $redirid ,
'pr_type' => 'pr_type' ,
'pr_level' => 'pr_level' ,
'pr_cascade' => 'pr_cascade' ,
'pr_user' => 'pr_user' ,
'pr_expiry' => 'pr_expiry'
),
array ( 'pr_page' => $pageid ),
__METHOD__ ,
array ( 'IGNORE' )
);
# Update the protection log
$log = new LogPage ( 'protect' );
2009-01-07 22:49:54 +00:00
$comment = wfMsgForContent ( 'prot_1movedto2' , $this -> getPrefixedText (), $nt -> getPrefixedText () );
2010-07-25 15:53:22 +00:00
if ( $reason ) {
$comment .= wfMsgForContent ( 'colon-separator' ) . $reason ;
}
2011-05-17 22:03:20 +00:00
// @todo FIXME: $params?
$log -> addEntry ( 'move_prot' , $nt , $comment , array ( $this -> getPrefixedText () ) );
2008-09-24 15:02:02 +00:00
}
2005-07-01 10:44:48 +00:00
2008-09-24 15:02:02 +00:00
# Update watchlists
2004-03-23 10:22:49 +00:00
$oldnamespace = $this -> getNamespace () & ~ 1 ;
$newnamespace = $nt -> getNamespace () & ~ 1 ;
$oldtitle = $this -> getDBkey ();
$newtitle = $nt -> getDBkey ();
2010-04-24 06:22:11 +00:00
if ( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
2004-03-23 10:22:49 +00:00
WatchedItem :: duplicateEntries ( $this , $nt );
}
2011-03-21 16:46:17 +00:00
$dbw -> commit ();
2011-06-17 16:05:35 +00:00
2005-06-01 02:31:45 +00:00
wfRunHooks ( 'TitleMoveComplete' , array ( & $this , & $nt , & $wgUser , $pageid , $redirid ) );
2004-03-23 10:22:49 +00:00
return true ;
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
2011-01-03 20:32:04 +00:00
* Move page to a title which is either a redirect to the
* source page or nonexistent
2004-09-30 05:21:20 +00:00
*
2011-02-02 11:00:09 +00:00
* @ param $nt Title the page to move to , which should be a redirect or nonexistent
2011-01-05 23:08:13 +00:00
* @ param $reason String The reason for the move
2011-02-02 11:00:09 +00:00
* @ param $createRedirect Bool Whether to leave a redirect at the old title . Ignored
* if the user doesn ' t have the suppressredirect right
2004-09-30 05:21:20 +00:00
*/
2011-09-29 22:08:00 +00:00
private function moveToInternal ( & $nt , $reason = '' , $createRedirect = true ) {
global $wgUser , $wgContLang ;
2009-09-26 08:05:58 +00:00
2011-09-12 14:58:41 +00:00
if ( $nt -> exists () ) {
$moveOverRedirect = true ;
2011-09-14 10:00:30 +00:00
$logType = 'move_redir' ;
2011-09-12 14:58:41 +00:00
} else {
$moveOverRedirect = false ;
$logType = 'move' ;
}
$redirectSuppressed = ! $createRedirect && $wgUser -> isAllowed ( 'suppressredirect' );
2011-01-03 20:32:04 +00:00
2011-09-12 14:58:41 +00:00
$logEntry = new ManualLogEntry ( 'move' , $logType );
$logEntry -> setPerformer ( $wgUser );
$logEntry -> setTarget ( $this );
$logEntry -> setComment ( $reason );
$logEntry -> setParameters ( array (
'4::target' => $nt -> getPrefixedText (),
'5::noredir' => $redirectSuppressed ? '1' : '0' ,
) );
2005-04-03 14:45:42 +00:00
2011-09-12 14:58:41 +00:00
$formatter = LogFormatter :: newFromEntry ( $logEntry );
$formatter -> setContext ( RequestContext :: newExtraneousContext ( $this ) );
$comment = $formatter -> getPlainActionText ();
2005-04-03 14:45:42 +00:00
if ( $reason ) {
2009-11-02 19:06:16 +00:00
$comment .= wfMsgForContent ( 'colon-separator' ) . $reason ;
2005-04-03 14:45:42 +00:00
}
2011-03-24 02:54:11 +00:00
# Truncate for whole multibyte characters.
$comment = $wgContLang -> truncate ( $comment , 255 );
2005-07-01 10:44:48 +00:00
2004-03-23 10:22:49 +00:00
$oldid = $this -> getArticleID ();
2008-07-31 07:39:05 +00:00
$latest = $this -> getLatestRevID ();
2009-08-18 10:43:22 +00:00
$dbw = wfGetDB ( DB_MASTER );
2011-12-30 15:54:11 +00:00
$newpage = WikiPage :: factory ( $nt );
2011-01-03 20:32:04 +00:00
2011-12-30 15:54:11 +00:00
if ( $moveOverRedirect ) {
2011-01-03 20:32:04 +00:00
$newid = $nt -> getArticleID ();
# Delete the old redirect. We don't save it to history since
# by definition if we've got here it's rather uninteresting.
# We have to remove it so that the next step doesn't trigger
# a conflict on the unique namespace+title index...
$dbw -> delete ( 'page' , array ( 'page_id' => $newid ), __METHOD__ );
2011-12-30 15:54:11 +00:00
$newpage -> doDeleteUpdates ( $newid );
2011-01-03 20:32:04 +00:00
}
2005-07-01 10:44:48 +00:00
2005-03-28 10:47:12 +00:00
# Save a null revision in the page's history notifying of the move
2009-01-20 22:32:25 +00:00
$nullRevision = Revision :: newNullRevision ( $dbw , $oldid , $comment , true );
2011-01-03 20:32:04 +00:00
if ( ! is_object ( $nullRevision ) ) {
throw new MWException ( 'No valid null revision produced in ' . __METHOD__ );
}
2008-05-17 15:53:58 +00:00
$nullRevId = $nullRevision -> insertOn ( $dbw );
2009-12-14 20:37:51 +00:00
2004-03-23 10:22:49 +00:00
# Change the name of the target page:
2004-12-19 08:00:50 +00:00
$dbw -> update ( 'page' ,
2005-07-01 10:44:48 +00:00
/* SET */ array (
2004-12-19 08:00:50 +00:00
'page_namespace' => $nt -> getNamespace (),
2005-03-28 10:47:12 +00:00
'page_title' => $nt -> getDBkey (),
2005-07-01 10:44:48 +00:00
),
2004-12-19 08:00:50 +00:00
/* WHERE */ array ( 'page_id' => $oldid ),
2009-09-26 08:05:58 +00:00
__METHOD__
2004-03-23 10:22:49 +00:00
);
2011-12-30 15:54:11 +00:00
$this -> resetArticleID ( 0 );
2008-02-09 11:45:53 +00:00
$nt -> resetArticleID ( $oldid );
2004-03-23 10:22:49 +00:00
2011-12-30 15:54:11 +00:00
$newpage -> updateRevisionOn ( $dbw , $nullRevision );
2011-08-10 18:32:22 +00:00
wfRunHooks ( 'NewRevisionFromEditComplete' ,
2011-12-30 15:54:11 +00:00
array ( $newpage , $nullRevision , $latest , $wgUser ) );
$newpage -> doEditUpdates ( $nullRevision , $wgUser , array ( 'changed' => false ) );
2011-05-07 10:24:38 +00:00
2004-12-10 05:56:44 +00:00
# Recreate the redirect, this time in the other direction.
2011-12-30 15:54:11 +00:00
if ( $redirectSuppressed ) {
WikiPage :: onArticleDelete ( $this );
} else {
2007-11-06 16:14:24 +00:00
$mwRedir = MagicWord :: get ( 'redirect' );
$redirectText = $mwRedir -> getSynonym ( 0 ) . ' [[' . $nt -> getPrefixedText () . " ]] \n " ;
2011-11-02 16:36:20 +00:00
$redirectArticle = WikiPage :: factory ( $this );
2007-11-06 16:14:24 +00:00
$newid = $redirectArticle -> insertOn ( $dbw );
2011-08-10 18:32:22 +00:00
if ( $newid ) { // sanity
$redirectRevision = new Revision ( array (
'page' => $newid ,
'comment' => $comment ,
'text' => $redirectText ) );
$redirectRevision -> insertOn ( $dbw );
$redirectArticle -> updateRevisionOn ( $dbw , $redirectRevision , 0 );
wfRunHooks ( 'NewRevisionFromEditComplete' ,
array ( $redirectArticle , $redirectRevision , false , $wgUser ) );
2011-12-30 15:54:11 +00:00
$redirectArticle -> doEditUpdates ( $redirectRevision , $wgUser , array ( 'created' => true ) );
2011-08-10 18:32:22 +00:00
}
2007-11-06 16:14:24 +00:00
}
2008-12-30 17:52:43 +00:00
2005-01-31 04:07:56 +00:00
# Log the move
2011-09-12 14:58:41 +00:00
$logid = $logEntry -> insert ();
$logEntry -> publish ( $logid );
2004-03-23 10:22:49 +00:00
}
2009-12-14 20:37:51 +00:00
2009-02-09 14:16:51 +00:00
/**
* Move this page ' s subpages to be subpages of $nt
2010-03-23 21:40:02 +00:00
*
2009-02-09 14:16:51 +00:00
* @ param $nt Title Move target
* @ param $auth bool Whether $wgUser ' s permissions should be checked
* @ param $reason string The reason for the move
2011-02-02 11:00:09 +00:00
* @ param $createRedirect bool Whether to create redirects from the old subpages to
* the new ones Ignored if the user doesn 't have the ' suppressredirect ' right
2009-02-09 14:16:51 +00:00
* @ return mixed array with old page titles as keys , and strings ( new page titles ) or
2011-02-02 11:00:09 +00:00
* arrays ( errors ) as values , or an error array with numeric indices if no pages
* were moved
2009-02-09 14:16:51 +00:00
*/
public function moveSubpages ( $nt , $auth = true , $reason = '' , $createRedirect = true ) {
2009-04-28 03:03:48 +00:00
global $wgMaximumMovedPages ;
2009-02-09 14:16:51 +00:00
// Check permissions
2010-07-25 15:53:22 +00:00
if ( ! $this -> userCan ( 'move-subpages' ) ) {
2009-02-09 14:16:51 +00:00
return array ( 'cant-move-subpages' );
2010-07-25 15:53:22 +00:00
}
2009-02-09 14:16:51 +00:00
// Do the source and target namespaces support subpages?
2010-07-25 15:53:22 +00:00
if ( ! MWNamespace :: hasSubpages ( $this -> getNamespace () ) ) {
2009-02-09 14:16:51 +00:00
return array ( 'namespace-nosubpages' ,
MWNamespace :: getCanonicalName ( $this -> getNamespace () ) );
2010-07-25 15:53:22 +00:00
}
if ( ! MWNamespace :: hasSubpages ( $nt -> getNamespace () ) ) {
2009-02-09 14:16:51 +00:00
return array ( 'namespace-nosubpages' ,
MWNamespace :: getCanonicalName ( $nt -> getNamespace () ) );
2010-07-25 15:53:22 +00:00
}
2009-02-09 14:16:51 +00:00
2010-04-24 06:22:11 +00:00
$subpages = $this -> getSubpages ( $wgMaximumMovedPages + 1 );
2009-02-09 14:16:51 +00:00
$retval = array ();
$count = 0 ;
2010-04-24 06:22:11 +00:00
foreach ( $subpages as $oldSubpage ) {
2009-02-09 14:16:51 +00:00
$count ++ ;
2010-04-24 06:22:11 +00:00
if ( $count > $wgMaximumMovedPages ) {
2009-02-09 14:16:51 +00:00
$retval [ $oldSubpage -> getPrefixedTitle ()] =
array ( 'movepage-max-pages' ,
$wgMaximumMovedPages );
break ;
}
2009-08-18 20:28:26 +00:00
// We don't know whether this function was called before
// or after moving the root page, so check both
// $this and $nt
2010-04-24 06:22:11 +00:00
if ( $oldSubpage -> getArticleId () == $this -> getArticleId () ||
2009-08-18 20:28:26 +00:00
$oldSubpage -> getArticleID () == $nt -> getArticleId () )
2010-07-25 15:53:22 +00:00
{
2009-02-09 14:16:51 +00:00
// When moving a page to a subpage of itself,
// don't move it twice
continue ;
2010-07-25 15:53:22 +00:00
}
2009-02-09 14:16:51 +00:00
$newPageName = preg_replace (
2010-04-24 06:22:11 +00:00
'#^' . preg_quote ( $this -> getDBkey (), '#' ) . '#' ,
2009-12-17 14:45:52 +00:00
StringUtils :: escapeRegexReplacement ( $nt -> getDBkey () ), # bug 21234
2009-10-22 14:50:45 +00:00
$oldSubpage -> getDBkey () );
2010-04-24 06:22:11 +00:00
if ( $oldSubpage -> isTalkPage () ) {
2009-02-09 14:16:51 +00:00
$newNs = $nt -> getTalkPage () -> getNamespace ();
} else {
$newNs = $nt -> getSubjectPage () -> getNamespace ();
}
# Bug 14385: we need makeTitleSafe because the new page names may
# be longer than 255 characters.
$newSubpage = Title :: makeTitleSafe ( $newNs , $newPageName );
$success = $oldSubpage -> moveTo ( $newSubpage , $auth , $reason , $createRedirect );
2010-04-24 06:22:11 +00:00
if ( $success === true ) {
2009-02-09 14:16:51 +00:00
$retval [ $oldSubpage -> getPrefixedText ()] = $newSubpage -> getPrefixedText ();
} else {
$retval [ $oldSubpage -> getPrefixedText ()] = $success ;
}
}
return $retval ;
}
2009-12-14 20:37:51 +00:00
2008-11-08 19:31:08 +00:00
/**
* Checks if this page is just a one - rev redirect .
* Adds lock , so don ' t use just for light purposes .
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2008-11-08 19:31:08 +00:00
*/
2008-11-13 22:20:51 +00:00
public function isSingleRevRedirect () {
2008-11-08 19:31:08 +00:00
$dbw = wfGetDB ( DB_MASTER );
# Is it a redirect?
$row = $dbw -> selectRow ( 'page' ,
2008-11-13 22:20:51 +00:00
array ( 'page_is_redirect' , 'page_latest' , 'page_id' ),
$this -> pageCond (),
2008-11-08 19:31:08 +00:00
__METHOD__ ,
2009-02-17 19:03:40 +00:00
array ( 'FOR UPDATE' )
2008-11-08 19:31:08 +00:00
);
# Cache some fields we may want
2010-04-24 06:22:11 +00:00
$this -> mArticleID = $row ? intval ( $row -> page_id ) : 0 ;
2008-11-08 19:31:08 +00:00
$this -> mRedirect = $row ? ( bool ) $row -> page_is_redirect : false ;
2010-04-24 06:22:11 +00:00
$this -> mLatestID = $row ? intval ( $row -> page_latest ) : false ;
if ( ! $this -> mRedirect ) {
2008-11-08 19:31:08 +00:00
return false ;
}
# Does the article have a history?
2010-04-24 06:22:11 +00:00
$row = $dbw -> selectField ( array ( 'page' , 'revision' ),
2008-11-08 19:31:08 +00:00
'rev_id' ,
array ( 'page_namespace' => $this -> getNamespace (),
'page_title' => $this -> getDBkey (),
'page_id=rev_page' ,
'page_latest != rev_id'
2009-12-14 20:37:51 +00:00
),
2008-11-08 19:31:08 +00:00
__METHOD__ ,
2009-02-17 19:03:40 +00:00
array ( 'FOR UPDATE' )
2008-11-08 19:31:08 +00:00
);
# Return true if there was no history
2010-04-24 06:22:11 +00:00
return ( $row === false );
2008-11-08 19:31:08 +00:00
}
2004-03-23 10:22:49 +00:00
2004-09-30 05:21:20 +00:00
/**
* Checks if $this can be moved to a given Title
* - Selects for update , so don ' t call it unless you mean business
*
2011-01-05 23:08:13 +00:00
* @ param $nt Title the new title to check
2011-02-02 11:00:09 +00:00
* @ return Bool
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function isValidMoveTarget ( $nt ) {
2010-07-23 17:11:20 +00:00
# Is it an existing file?
2010-04-24 06:22:11 +00:00
if ( $nt -> getNamespace () == NS_FILE ) {
2008-05-03 13:09:34 +00:00
$file = wfLocalFile ( $nt );
2010-04-24 06:22:11 +00:00
if ( $file -> exists () ) {
2008-05-03 13:09:34 +00:00
wfDebug ( __METHOD__ . " : file exists \n " );
return false ;
}
}
2008-11-08 19:31:08 +00:00
# Is it a redirect with no history?
2010-04-24 06:22:11 +00:00
if ( ! $nt -> isSingleRevRedirect () ) {
2008-11-08 19:31:08 +00:00
wfDebug ( __METHOD__ . " : not a one-rev redirect \n " );
2005-07-01 10:44:48 +00:00
return false ;
2004-03-23 10:22:49 +00:00
}
2008-11-08 19:31:08 +00:00
# Get the article text
$rev = Revision :: newFromTitle ( $nt );
2011-09-29 23:11:31 +00:00
if ( ! is_object ( $rev ) ){
return false ;
}
2008-11-08 19:31:08 +00:00
$text = $rev -> getText ();
2004-03-23 10:22:49 +00:00
# Does the redirect point to the source?
2006-06-28 20:31:04 +00:00
# Or is it a broken self-redirect, usually caused by namespace collisions?
2006-11-29 11:43:58 +00:00
$m = array ();
2005-09-02 04:44:15 +00:00
if ( preg_match ( " / \\ [ \\ [ \\ s*([^ \\ ] \\ |]*)]]/ " , $text , $m ) ) {
2004-03-23 10:22:49 +00:00
$redirTitle = Title :: newFromText ( $m [ 1 ] );
2010-04-24 06:22:11 +00:00
if ( ! is_object ( $redirTitle ) ||
2006-06-28 20:31:04 +00:00
( $redirTitle -> getPrefixedDBkey () != $this -> getPrefixedDBkey () &&
$redirTitle -> getPrefixedDBkey () != $nt -> getPrefixedDBkey () ) ) {
wfDebug ( __METHOD__ . " : redirect points to other page \n " );
2004-03-23 10:22:49 +00:00
return false ;
}
2005-09-02 04:44:15 +00:00
} else {
# Fail safe
2006-06-28 20:31:04 +00:00
wfDebug ( __METHOD__ . " : failsafe \n " );
2005-09-02 04:44:15 +00:00
return false ;
2004-03-23 10:22:49 +00:00
}
2008-11-08 19:31:08 +00:00
return true ;
2004-03-23 10:22:49 +00:00
}
2008-04-14 07:45:50 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get categories to which this Title belongs and return an array of
* categories ' names .
*
2011-01-05 23:08:13 +00:00
* @ return Array of parents in the form :
2011-02-02 11:00:09 +00:00
* $parent => $currentarticle
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getParentCategories () {
2006-03-07 01:10:39 +00:00
global $wgContLang ;
2005-07-01 10:44:48 +00:00
2011-01-26 20:03:38 +00:00
$data = array ();
$titleKey = $this -> getArticleId ();
if ( $titleKey === 0 ) {
return $data ;
}
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2004-07-10 03:09:26 +00:00
2011-01-24 15:30:35 +00:00
$res = $dbr -> select ( 'categorylinks' , '*' ,
array (
'cl_from' => $titleKey ,
),
__METHOD__ ,
2011-01-26 20:03:38 +00:00
array ()
2011-01-24 15:30:35 +00:00
);
2005-07-01 10:44:48 +00:00
2010-04-24 06:22:11 +00:00
if ( $dbr -> numRows ( $res ) > 0 ) {
2010-09-01 23:58:26 +00:00
foreach ( $res as $row ) {
2010-04-24 06:22:11 +00:00
// $data[] = Title::newFromText($wgContLang->getNSText ( NS_CATEGORY ).':'.$row->cl_to);
$data [ $wgContLang -> getNSText ( NS_CATEGORY ) . ':' . $row -> cl_to ] = $this -> getFullText ();
2010-09-01 23:58:26 +00:00
}
2004-06-06 02:06:46 +00:00
}
return $data ;
}
2004-06-07 07:55:27 +00:00
2004-09-30 05:21:20 +00:00
/**
2004-11-20 03:35:00 +00:00
* Get a tree of parent categories
2010-03-23 21:40:02 +00:00
*
2011-01-05 23:08:13 +00:00
* @ param $children Array with the children in the keys , to check for circular refs
* @ return Array Tree of parent categories
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function getParentCategoryTree ( $children = array () ) {
2009-12-14 20:37:51 +00:00
$stack = array ();
2004-08-23 00:49:02 +00:00
$parents = $this -> getParentCategories ();
2005-07-01 10:44:48 +00:00
2010-04-24 06:22:11 +00:00
if ( $parents ) {
foreach ( $parents as $parent => $current ) {
2004-11-20 03:35:00 +00:00
if ( array_key_exists ( $parent , $children ) ) {
# Circular reference
$stack [ $parent ] = array ();
} else {
2010-04-24 06:22:11 +00:00
$nt = Title :: newFromText ( $parent );
2006-08-24 17:35:42 +00:00
if ( $nt ) {
2010-04-24 06:22:11 +00:00
$stack [ $parent ] = $nt -> getParentCategoryTree ( $children + array ( $parent => 1 ) );
2006-08-24 17:35:42 +00:00
}
2004-11-20 03:35:00 +00:00
}
2004-06-06 02:06:46 +00:00
}
}
2010-09-02 20:48:02 +00:00
return $stack ;
2004-06-06 02:06:46 +00:00
}
2005-07-01 10:44:48 +00:00
2004-09-30 05:21:20 +00:00
/**
* Get an associative array for selecting this title from
2006-01-09 03:52:24 +00:00
* the " page " table
2004-09-30 05:21:20 +00:00
*
2011-02-02 11:00:09 +00:00
* @ return Array suitable for the $where parameter of DB :: select ()
2004-09-30 05:21:20 +00:00
*/
2007-04-16 15:24:04 +00:00
public function pageCond () {
2010-04-24 06:22:11 +00:00
if ( $this -> mArticleID > 0 ) {
2008-12-08 23:10:24 +00:00
// PK avoids secondary lookups in InnoDB, shouldn't hurt other DBs
return array ( 'page_id' => $this -> mArticleID );
2008-12-08 22:30:58 +00:00
} else {
return array ( 'page_namespace' => $this -> mNamespace , 'page_title' => $this -> mDbkeyform );
}
2004-07-18 08:48:43 +00:00
}
2004-10-02 19:49:54 +00:00
/**
* Get the revision ID of the previous revision
*
2011-01-05 23:08:13 +00:00
* @ param $revId Int Revision ID . Get the revision that was before this one .
* @ param $flags Int Title :: GAID_FOR_UPDATE
2011-02-02 11:00:09 +00:00
* @ return Int | Bool Old revision ID , or FALSE if none exists
2004-10-02 19:49:54 +00:00
*/
2010-04-24 06:22:11 +00:00
public function getPreviousRevisionID ( $revId , $flags = 0 ) {
2010-09-30 19:13:25 +00:00
$db = ( $flags & self :: GAID_FOR_UPDATE ) ? wfGetDB ( DB_MASTER ) : wfGetDB ( DB_SLAVE );
2008-05-15 14:53:02 +00:00
return $db -> selectField ( 'revision' , 'rev_id' ,
2008-05-23 09:00:08 +00:00
array (
2010-04-24 06:22:11 +00:00
'rev_page' => $this -> getArticleId ( $flags ),
2008-07-31 07:39:05 +00:00
'rev_id < ' . intval ( $revId )
2008-05-23 09:00:08 +00:00
),
__METHOD__ ,
array ( 'ORDER BY' => 'rev_id DESC' )
);
2004-10-02 19:49:54 +00:00
}
/**
* Get the revision ID of the next revision
*
2011-01-05 23:08:13 +00:00
* @ param $revId Int Revision ID . Get the revision that was after this one .
* @ param $flags Int Title :: GAID_FOR_UPDATE
2011-02-02 11:00:09 +00:00
* @ return Int | Bool Next revision ID , or FALSE if none exists
2004-10-02 19:49:54 +00:00
*/
2010-04-24 06:22:11 +00:00
public function getNextRevisionID ( $revId , $flags = 0 ) {
2010-09-30 19:13:25 +00:00
$db = ( $flags & self :: GAID_FOR_UPDATE ) ? wfGetDB ( DB_MASTER ) : wfGetDB ( DB_SLAVE );
2008-05-15 14:53:02 +00:00
return $db -> selectField ( 'revision' , 'rev_id' ,
2008-05-23 09:00:08 +00:00
array (
2010-04-24 06:22:11 +00:00
'rev_page' => $this -> getArticleId ( $flags ),
2008-07-31 07:39:05 +00:00
'rev_id > ' . intval ( $revId )
2008-05-23 09:00:08 +00:00
),
__METHOD__ ,
array ( 'ORDER BY' => 'rev_id' )
);
2004-10-02 19:49:54 +00:00
}
2009-12-14 20:37:51 +00:00
2009-01-15 17:53:19 +00:00
/**
* Get the first revision of the page
*
2011-01-05 23:08:13 +00:00
* @ param $flags Int Title :: GAID_FOR_UPDATE
2011-02-02 11:00:09 +00:00
* @ return Revision | Null if page doesn ' t exist
2009-01-15 17:53:19 +00:00
*/
2010-04-24 06:22:11 +00:00
public function getFirstRevision ( $flags = 0 ) {
$pageId = $this -> getArticleId ( $flags );
2011-04-05 20:42:45 +00:00
if ( $pageId ) {
$db = ( $flags & self :: GAID_FOR_UPDATE ) ? wfGetDB ( DB_MASTER ) : wfGetDB ( DB_SLAVE );
$row = $db -> selectRow ( 'revision' , '*' ,
array ( 'rev_page' => $pageId ),
__METHOD__ ,
array ( 'ORDER BY' => 'rev_timestamp ASC' , 'LIMIT' => 1 )
);
if ( $row ) {
return new Revision ( $row );
}
2009-01-15 17:53:19 +00:00
}
2011-04-05 20:42:45 +00:00
return null ;
2009-01-15 17:53:19 +00:00
}
2009-12-14 20:37:51 +00:00
2008-12-21 09:33:04 +00:00
/**
2011-04-05 20:42:45 +00:00
* Get the oldest revision timestamp of this page
2008-12-21 09:33:04 +00:00
*
2011-04-05 20:42:45 +00:00
* @ param $flags Int Title :: GAID_FOR_UPDATE
* @ return String : MW timestamp
2008-12-21 09:33:04 +00:00
*/
2011-04-05 20:42:45 +00:00
public function getEarliestRevTime ( $flags = 0 ) {
2011-06-17 16:05:35 +00:00
$rev = $this -> getFirstRevision ( $flags );
2011-04-05 20:42:45 +00:00
return $rev ? $rev -> getTimestamp () : null ;
2008-12-21 09:33:04 +00:00
}
2005-07-01 10:44:48 +00:00
2012-01-03 21:44:41 +00:00
/**
* Check if this is a new page
*
* @ return bool
*/
public function isNewPage () {
$dbr = wfGetDB ( DB_SLAVE );
return ( bool ) $dbr -> selectField ( 'page' , 'page_is_new' , $this -> pageCond (), __METHOD__ );
}
2011-12-27 15:29:44 +00:00
/**
* Check whether the number of revisions of this page surpasses $wgDeleteRevisionsLimit
*
* @ return bool
*/
public function isBigDeletion () {
global $wgDeleteRevisionsLimit ;
if ( ! $wgDeleteRevisionsLimit ) {
return false ;
}
$revCount = $this -> estimateRevisionCount ();
return $revCount > $wgDeleteRevisionsLimit ;
}
/**
* Get the approximate revision count of this page .
*
* @ return int
*/
public function estimateRevisionCount () {
if ( ! $this -> exists () ) {
return 0 ;
}
if ( $this -> mEstimateRevisions === null ) {
$dbr = wfGetDB ( DB_SLAVE );
$this -> mEstimateRevisions = $dbr -> estimateRowCount ( 'revision' , '*' ,
array ( 'rev_page' => $this -> getArticleId () ), __METHOD__ );
}
return $this -> mEstimateRevisions ;
}
2006-12-06 08:28:44 +00:00
/**
2011-04-05 20:42:45 +00:00
* Get the number of revisions between the given revision .
2008-04-10 17:55:12 +00:00
* Used for diffs and other things that really need it .
2006-12-06 08:28:44 +00:00
*
2011-04-05 20:42:45 +00:00
* @ param $old int | Revision Old revision or rev ID ( first before range )
* @ param $new int | Revision New revision or rev ID ( first after range )
* @ return Int Number of revisions between these revisions .
2006-12-06 08:28:44 +00:00
*/
2007-04-16 15:24:04 +00:00
public function countRevisionsBetween ( $old , $new ) {
2011-04-05 20:42:45 +00:00
if ( ! ( $old instanceof Revision ) ) {
$old = Revision :: newFromTitle ( $this , ( int ) $old );
}
if ( ! ( $new instanceof Revision ) ) {
$new = Revision :: newFromTitle ( $this , ( int ) $new );
}
if ( ! $old || ! $new ) {
return 0 ; // nothing to compare
}
2007-01-22 23:50:42 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2011-04-05 20:42:45 +00:00
return ( int ) $dbr -> selectField ( 'revision' , 'count(*)' ,
array (
'rev_page' => $this -> getArticleId (),
'rev_timestamp > ' . $dbr -> addQuotes ( $dbr -> timestamp ( $old -> getTimestamp () ) ),
'rev_timestamp < ' . $dbr -> addQuotes ( $dbr -> timestamp ( $new -> getTimestamp () ) )
),
__METHOD__
2009-02-28 11:58:06 +00:00
);
2006-12-06 08:28:44 +00:00
}
2010-11-18 00:08:37 +00:00
/**
* Get the number of authors between the given revision IDs .
* Used for diffs and other things that really need it .
*
2011-04-05 20:42:45 +00:00
* @ param $old int | Revision Old revision or rev ID ( first before range )
* @ param $new int | Revision New revision or rev ID ( first after range )
2011-01-05 23:08:13 +00:00
* @ param $limit Int Maximum number of authors
2011-04-05 20:42:45 +00:00
* @ return Int Number of revision authors between these revisions .
2010-11-18 00:08:37 +00:00
*/
2011-04-05 20:42:45 +00:00
public function countAuthorsBetween ( $old , $new , $limit ) {
if ( ! ( $old instanceof Revision ) ) {
$old = Revision :: newFromTitle ( $this , ( int ) $old );
}
if ( ! ( $new instanceof Revision ) ) {
$new = Revision :: newFromTitle ( $this , ( int ) $new );
}
if ( ! $old || ! $new ) {
return 0 ; // nothing to compare
}
$dbr = wfGetDB ( DB_SLAVE );
$res = $dbr -> select ( 'revision' , 'DISTINCT rev_user_text' ,
2010-11-18 00:08:37 +00:00
array (
2010-12-12 16:18:08 +00:00
'rev_page' => $this -> getArticleID (),
2011-04-05 20:42:45 +00:00
'rev_timestamp > ' . $dbr -> addQuotes ( $dbr -> timestamp ( $old -> getTimestamp () ) ),
'rev_timestamp < ' . $dbr -> addQuotes ( $dbr -> timestamp ( $new -> getTimestamp () ) )
2010-11-18 00:08:37 +00:00
), __METHOD__ ,
2011-04-05 20:42:45 +00:00
array ( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated
2010-11-18 00:08:37 +00:00
);
2011-04-05 20:42:45 +00:00
return ( int ) $dbr -> numRows ( $res );
2010-11-18 00:08:37 +00:00
}
2005-02-21 11:28:07 +00:00
/**
* Compare with another title .
*
2011-01-05 23:08:13 +00:00
* @ param $title Title
2011-02-02 11:00:09 +00:00
* @ return Bool
2005-02-21 11:28:07 +00:00
*/
2008-07-28 22:52:14 +00:00
public function equals ( Title $title ) {
2006-02-18 19:41:01 +00:00
// Note: === is necessary for proper matching of number-like titles.
return $this -> getInterwiki () === $title -> getInterwiki ()
2005-02-21 11:28:07 +00:00
&& $this -> getNamespace () == $title -> getNamespace ()
2008-01-14 09:13:04 +00:00
&& $this -> getDBkey () === $title -> getDBkey ();
2005-02-21 11:28:07 +00:00
}
2008-04-14 07:45:50 +00:00
2011-09-01 08:34:29 +00:00
/**
* Check if this title is a subpage of another title
*
* @ param $title Title
* @ return Bool
*/
public function isSubpageOf ( Title $title ) {
return $this -> getInterwiki () === $title -> getInterwiki ()
&& $this -> getNamespace () == $title -> getNamespace ()
&& strpos ( $this -> getDBkey (), $title -> getDBkey () . '/' ) === 0 ;
}
2005-04-25 09:25:06 +00:00
/**
2008-12-13 02:40:53 +00:00
* Check if page exists . For historical reasons , this function simply
* checks for the existence of the title in the page table , and will
* thus return false for interwiki links , special pages and the like .
* If you want to know if a title can be meaningfully viewed , you should
* probably call the isKnown () method instead .
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2005-04-25 09:25:06 +00:00
*/
2007-04-16 15:24:04 +00:00
public function exists () {
2005-04-25 09:25:06 +00:00
return $this -> getArticleId () != 0 ;
}
2004-10-02 19:49:54 +00:00
2005-04-27 07:48:14 +00:00
/**
2008-12-13 02:40:53 +00:00
* Should links to this title be shown as potentially viewable ( i . e . as
* " bluelinks " ), even if there ' s no record by this title in the page
* table ?
*
* This function is semi - deprecated for public use , as well as somewhat
* misleadingly named . You probably just want to call isKnown (), which
* calls this function internally .
2005-07-01 10:44:48 +00:00
*
2008-12-13 04:14:40 +00:00
* ( ISSUE : Most of these checks are cheap , but the file existence check
* can potentially be quite expensive . Including it here fixes a lot of
* existing code , but we might want to add an optional parameter to skip
* it and any other expensive checks . )
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2005-04-27 07:48:14 +00:00
*/
2007-04-16 15:24:04 +00:00
public function isAlwaysKnown () {
2012-02-12 23:10:06 +00:00
$isKnown = null ;
/**
* Allows overriding default behaviour for determining if a page exists .
* If $isKnown is kept as null , regular checks happen . If it ' s
* a boolean , this value is returned by the isKnown method .
*
* @ since 1.20
*
* @ param Title $title
* @ param boolean | null $isKnown
*/
wfRunHooks ( 'TitleIsAlwaysKnown' , array ( $this , & $isKnown ) );
if ( ! is_null ( $isKnown ) ) {
return $isKnown ;
}
2010-04-24 06:22:11 +00:00
if ( $this -> mInterwiki != '' ) {
2008-12-13 02:40:53 +00:00
return true ; // any interwiki link might be viewable, for all we know
}
2012-02-12 23:10:06 +00:00
2009-02-25 00:48:07 +00:00
switch ( $this -> mNamespace ) {
2010-09-02 20:48:02 +00:00
case NS_MEDIA :
case NS_FILE :
2011-02-02 11:00:09 +00:00
// file exists, possibly in a foreign repo
return ( bool ) wfFindFile ( $this );
2010-09-02 20:48:02 +00:00
case NS_SPECIAL :
2011-02-02 11:00:09 +00:00
// valid special page
2011-04-17 11:31:11 +00:00
return SpecialPageFactory :: exists ( $this -> getDBkey () );
2010-09-02 20:48:02 +00:00
case NS_MAIN :
2011-02-02 11:00:09 +00:00
// selflink, possibly with fragment
return $this -> mDbkeyform == '' ;
2010-09-02 20:48:02 +00:00
case NS_MEDIAWIKI :
2011-02-02 11:00:09 +00:00
// known system message
2011-09-22 14:04:08 +00:00
return $this -> hasSourceText () !== false ;
2010-09-02 20:48:02 +00:00
default :
return false ;
2008-12-13 02:40:53 +00:00
}
}
/**
* Does this title refer to a page that can ( or might ) be meaningfully
* viewed ? In particular , this function may be used to determine if
* links to the title should be rendered as " bluelinks " ( as opposed to
* " redlinks " to non - existent pages ) .
2012-02-12 23:42:39 +00:00
* Adding something else to this function will cause inconsistency
* since LinkHolderArray calls isAlwaysKnown () and does its own
* page existence check .
2008-12-13 02:40:53 +00:00
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2008-12-13 02:40:53 +00:00
*/
public function isKnown () {
2012-02-12 23:10:06 +00:00
return $this -> isAlwaysKnown () || $this -> exists ();
2005-04-27 07:48:14 +00:00
}
2009-12-14 20:37:51 +00:00
2010-04-02 08:00:40 +00:00
/**
* Does this page have source text ?
*
* @ return Boolean
*/
public function hasSourceText () {
2010-07-25 15:53:22 +00:00
if ( $this -> exists () ) {
2010-04-02 08:00:40 +00:00
return true ;
2010-07-25 15:53:22 +00:00
}
2010-04-02 08:00:40 +00:00
if ( $this -> mNamespace == NS_MEDIAWIKI ) {
2010-04-03 21:07:57 +00:00
// If the page doesn't exist but is a known system message, default
2011-09-22 14:04:08 +00:00
// message content will be displayed, same for language subpages-
// Use always content language to avoid loading hundreds of languages
// to get the link color.
global $wgContLang ;
list ( $name , $lang ) = MessageCache :: singleton () -> figureMessage ( $wgContLang -> lcfirst ( $this -> getText () ) );
$message = wfMessage ( $name ) -> inLanguage ( $wgContLang ) -> useDatabase ( false );
return $message -> exists ();
2010-04-02 08:00:40 +00:00
}
return false ;
}
2011-02-05 15:11:52 +00:00
/**
* Get the default message text or false if the message doesn ' t exist
*
* @ return String or false
*/
public function getDefaultMessageText () {
global $wgContLang ;
if ( $this -> getNamespace () != NS_MEDIAWIKI ) { // Just in case
return false ;
}
list ( $name , $lang ) = MessageCache :: singleton () -> figureMessage ( $wgContLang -> lcfirst ( $this -> getText () ) );
$message = wfMessage ( $name ) -> inLanguage ( $lang ) -> useDatabase ( false );
if ( $message -> exists () ) {
return $message -> plain ();
} else {
return false ;
}
}
2009-01-02 19:52:57 +00:00
/**
2011-12-11 14:48:45 +00:00
* Updates page_touched for this page ; called from LinksUpdate . php
2010-07-25 15:53:22 +00:00
*
2011-12-11 14:48:45 +00:00
* @ return Bool true if the update succeded
2010-07-25 15:53:22 +00:00
*/
2011-12-11 14:48:45 +00:00
public function invalidateCache () {
if ( wfReadOnly () ) {
return false ;
}
$dbw = wfGetDB ( DB_MASTER );
$success = $dbw -> update (
'page' ,
array ( 'page_touched' => $dbw -> timestamp () ),
$this -> pageCond (),
__METHOD__
);
HTMLFileCache :: clearFileCache ( $this );
return $success ;
2009-01-02 19:52:57 +00:00
}
2005-05-26 10:23:36 +00:00
/**
2006-06-18 12:42:16 +00:00
* Update page_touched timestamps and send squid purge messages for
2008-04-14 07:45:50 +00:00
* pages linking to this title . May be sent to the job queue depending
2006-06-18 12:42:16 +00:00
* on the number of links . Typically called on create and delete .
2005-05-26 10:23:36 +00:00
*/
2007-04-16 15:24:04 +00:00
public function touchLinks () {
2006-06-18 12:42:16 +00:00
$u = new HTMLCacheUpdate ( $this , 'pagelinks' );
$u -> doUpdate ();
2006-01-07 13:31:29 +00:00
2006-06-18 12:42:16 +00:00
if ( $this -> getNamespace () == NS_CATEGORY ) {
$u = new HTMLCacheUpdate ( $this , 'categorylinks' );
$u -> doUpdate ();
2005-12-20 21:17:03 +00:00
}
2005-05-26 10:23:36 +00:00
}
2005-07-23 05:47:25 +00:00
2012-01-03 21:44:41 +00:00
/**
* Get the last touched timestamp
*
* @ param $db DatabaseBase : optional db
* @ return String last - touched timestamp
*/
public function getTouched ( $db = null ) {
$db = isset ( $db ) ? $db : wfGetDB ( DB_SLAVE );
$touched = $db -> selectField ( 'page' , 'page_touched' , $this -> pageCond (), __METHOD__ );
return $touched ;
}
2008-11-08 19:31:08 +00:00
/**
* Get the timestamp when this page was updated since the user last saw it .
2010-03-23 21:40:02 +00:00
*
* @ param $user User
2011-02-02 11:00:09 +00:00
* @ return String | Null
2008-11-08 19:31:08 +00:00
*/
2009-12-11 21:07:27 +00:00
public function getNotificationTimestamp ( $user = null ) {
2008-11-08 19:31:08 +00:00
global $wgUser , $wgShowUpdatedMarker ;
// Assume current user if none given
2010-07-25 15:53:22 +00:00
if ( ! $user ) {
$user = $wgUser ;
}
2008-11-08 19:31:08 +00:00
// Check cache first
$uid = $user -> getId ();
2011-02-12 01:10:48 +00:00
// avoid isset here, as it'll return false for null entries
if ( array_key_exists ( $uid , $this -> mNotificationTimestamp ) ) {
2008-11-08 19:31:08 +00:00
return $this -> mNotificationTimestamp [ $uid ];
}
2010-04-24 06:22:11 +00:00
if ( ! $uid || ! $wgShowUpdatedMarker ) {
2008-11-08 19:31:08 +00:00
return $this -> mNotificationTimestamp [ $uid ] = false ;
}
// Don't cache too much!
2010-04-24 06:22:11 +00:00
if ( count ( $this -> mNotificationTimestamp ) >= self :: CACHE_MAX ) {
2008-11-08 19:31:08 +00:00
$this -> mNotificationTimestamp = array ();
}
$dbr = wfGetDB ( DB_SLAVE );
$this -> mNotificationTimestamp [ $uid ] = $dbr -> selectField ( 'watchlist' ,
'wl_notificationtimestamp' ,
array ( 'wl_namespace' => $this -> getNamespace (),
'wl_title' => $this -> getDBkey (),
'wl_user' => $user -> getId ()
),
__METHOD__
);
return $this -> mNotificationTimestamp [ $uid ];
}
2006-09-05 14:44:50 +00:00
2006-03-16 02:51:11 +00:00
/**
* Generate strings used for xml 'id' names in monobook tabs
2010-03-23 21:40:02 +00:00
*
2010-03-15 23:22:50 +00:00
* @ param $prepend string defaults to 'nstab-'
2011-01-05 23:08:13 +00:00
* @ return String XML 'id' name
2006-03-16 02:51:11 +00:00
*/
2009-07-06 22:43:04 +00:00
public function getNamespaceKey ( $prepend = 'nstab-' ) {
2009-10-16 04:06:30 +00:00
global $wgContLang ;
2009-07-06 22:43:04 +00:00
// Gets the subject namespace if this title
$namespace = MWNamespace :: getSubject ( $this -> getNamespace () );
// Checks if cononical namespace name exists for namespace
2009-10-16 04:06:30 +00:00
if ( MWNamespace :: exists ( $this -> getNamespace () ) ) {
2009-07-06 22:43:04 +00:00
// Uses canonical namespace name
2009-10-16 04:06:30 +00:00
$namespaceKey = MWNamespace :: getCanonicalName ( $namespace );
2009-07-06 22:43:04 +00:00
} else {
// Uses text of namespace
$namespaceKey = $this -> getSubjectNsText ();
}
// Makes namespace key lowercase
$namespaceKey = $wgContLang -> lc ( $namespaceKey );
// Uses main
if ( $namespaceKey == '' ) {
$namespaceKey = 'main' ;
}
// Changes file to image for backwards compatibility
if ( $namespaceKey == 'file' ) {
$namespaceKey = 'image' ;
2006-03-16 02:51:11 +00:00
}
2009-07-06 22:43:04 +00:00
return $prepend . $namespaceKey ;
2006-03-16 02:51:11 +00:00
}
2006-10-30 06:25:31 +00:00
2008-08-11 04:39:00 +00:00
/**
* Get all extant redirects to this Title
*
2011-02-02 11:00:09 +00:00
* @ param $ns Int | Null Single namespace to consider ; NULL to consider all namespaces
2011-01-05 23:08:13 +00:00
* @ return Array of Title redirects to this title
2008-08-11 04:39:00 +00:00
*/
2008-05-15 19:17:21 +00:00
public function getRedirectsHere ( $ns = null ) {
2008-04-12 18:06:57 +00:00
$redirs = array ();
2009-12-14 20:37:51 +00:00
$dbr = wfGetDB ( DB_SLAVE );
2008-05-15 19:17:21 +00:00
$where = array (
'rd_namespace' => $this -> getNamespace (),
'rd_title' => $this -> getDBkey (),
'rd_from = page_id'
);
2010-07-25 15:53:22 +00:00
if ( ! is_null ( $ns ) ) {
$where [ 'page_namespace' ] = $ns ;
}
2009-12-14 20:37:51 +00:00
2008-07-27 18:59:46 +00:00
$res = $dbr -> select (
2008-05-15 19:17:21 +00:00
array ( 'redirect' , 'page' ),
array ( 'page_namespace' , 'page_title' ),
$where ,
__METHOD__
);
2010-04-24 06:22:11 +00:00
foreach ( $res as $row ) {
2008-04-12 18:06:57 +00:00
$redirs [] = self :: newFromRow ( $row );
}
return $redirs ;
}
2009-12-14 20:37:51 +00:00
2009-01-21 20:42:32 +00:00
/**
* Check if this Title is a valid redirect target
*
2011-01-05 23:08:13 +00:00
* @ return Bool
2009-01-21 20:42:32 +00:00
*/
public function isValidRedirectTarget () {
global $wgInvalidRedirectTargets ;
2009-12-14 20:37:51 +00:00
2009-01-21 20:42:32 +00:00
// invalid redirect targets are stored in a global array, but explicity disallow Userlogout here
2010-04-24 06:22:11 +00:00
if ( $this -> isSpecial ( 'Userlogout' ) ) {
2009-01-21 20:42:32 +00:00
return false ;
}
2009-12-14 20:37:51 +00:00
2010-04-24 06:22:11 +00:00
foreach ( $wgInvalidRedirectTargets as $target ) {
if ( $this -> isSpecial ( $target ) ) {
2009-01-21 20:42:32 +00:00
return false ;
}
}
2009-12-14 20:37:51 +00:00
2009-01-21 20:42:32 +00:00
return true ;
}
2009-02-16 14:26:34 +00:00
/**
* Get a backlink cache object
2010-03-23 21:40:02 +00:00
*
2011-11-20 18:02:38 +00:00
* @ return BacklinkCache
2009-02-16 14:26:34 +00:00
*/
function getBacklinkCache () {
if ( is_null ( $this -> mBacklinkCache ) ) {
$this -> mBacklinkCache = new BacklinkCache ( $this );
}
return $this -> mBacklinkCache ;
}
2009-08-31 19:19:12 +00:00
/**
2011-02-02 11:00:09 +00:00
* Whether the magic words __INDEX__ and __NOINDEX__ function for this page .
2010-03-23 21:40:02 +00:00
*
* @ return Boolean
2009-08-31 19:19:12 +00:00
*/
2010-04-24 06:22:11 +00:00
public function canUseNoindex () {
2010-07-25 15:53:22 +00:00
global $wgContentNamespaces , $wgExemptFromUserRobotsControl ;
2009-08-31 19:19:12 +00:00
$bannedNamespaces = is_null ( $wgExemptFromUserRobotsControl )
? $wgContentNamespaces
: $wgExemptFromUserRobotsControl ;
return ! in_array ( $this -> mNamespace , $bannedNamespaces );
}
2009-12-14 20:37:51 +00:00
Reconcept cl_raw_sortkey as cl_sortkey_prefix
In response to feedback by Phillipe Verdy on bug 164. Now if a bunch of
pages have [[Category:Foo| ]], they'll sort amongst themselves according
to page name, instead of in basically random order as it is currently.
This also makes storage more elegant and intuitive: instead of giving
NULL a magic meaning when there's no custom sortkey specified, we just
store an empty string, since there's no prefix.
This means {{defaultsort:}} really now means {{defaultsortprefix:}},
which is slightly confusing, and a lot of code is now slightly
misleading or poorly named. But it should all work fine.
Also, while I was at it, I made updateCollation.php work as a transition
script, so you can apply the SQL patch and then run updateCollation.php
and things will work. However, with the new schema it's not trivial to
reverse this -- you'd have to recover the raw sort keys with some PHP.
Conversion goes at about a thousand rows a second for me, and seems to
be CPU-bound. Could probably be optimized.
I also adjusted the transition script so it will fix rows with collation
versions *greater* than the current one, as well as less. Thus if some
site wants to use their own collation, they can call it 137 or
something, and if they later want to switch back to MediaWiki stock
collation 7, it will work.
Also fixed a silly bug in updateCollation.php where it would say "1000
done" if it did nothing, and changed $res->numRows() >= self::BATCH_SIZE
to == so people don't wonder how it could be bigger (since it can't, I
hope).
2010-07-26 19:27:13 +00:00
/**
2010-07-26 22:04:19 +00:00
* Returns the raw sort key to be used for categories , with the specified
2011-01-17 14:02:22 +00:00
* prefix . This will be fed to Collation :: getSortKey () to get a
2010-07-26 22:04:19 +00:00
* binary sortkey that can be used for actual sorting .
Reconcept cl_raw_sortkey as cl_sortkey_prefix
In response to feedback by Phillipe Verdy on bug 164. Now if a bunch of
pages have [[Category:Foo| ]], they'll sort amongst themselves according
to page name, instead of in basically random order as it is currently.
This also makes storage more elegant and intuitive: instead of giving
NULL a magic meaning when there's no custom sortkey specified, we just
store an empty string, since there's no prefix.
This means {{defaultsort:}} really now means {{defaultsortprefix:}},
which is slightly confusing, and a lot of code is now slightly
misleading or poorly named. But it should all work fine.
Also, while I was at it, I made updateCollation.php work as a transition
script, so you can apply the SQL patch and then run updateCollation.php
and things will work. However, with the new schema it's not trivial to
reverse this -- you'd have to recover the raw sort keys with some PHP.
Conversion goes at about a thousand rows a second for me, and seems to
be CPU-bound. Could probably be optimized.
I also adjusted the transition script so it will fix rows with collation
versions *greater* than the current one, as well as less. Thus if some
site wants to use their own collation, they can call it 137 or
something, and if they later want to switch back to MediaWiki stock
collation 7, it will work.
Also fixed a silly bug in updateCollation.php where it would say "1000
done" if it did nothing, and changed $res->numRows() >= self::BATCH_SIZE
to == so people don't wonder how it could be bigger (since it can't, I
hope).
2010-07-26 19:27:13 +00:00
*
2010-07-26 22:04:19 +00:00
* @ param $prefix string The prefix to be used , specified using
* {{ defaultsort : }} or like [[ Category : Foo | prefix ]] . Empty for no
* prefix .
Reconcept cl_raw_sortkey as cl_sortkey_prefix
In response to feedback by Phillipe Verdy on bug 164. Now if a bunch of
pages have [[Category:Foo| ]], they'll sort amongst themselves according
to page name, instead of in basically random order as it is currently.
This also makes storage more elegant and intuitive: instead of giving
NULL a magic meaning when there's no custom sortkey specified, we just
store an empty string, since there's no prefix.
This means {{defaultsort:}} really now means {{defaultsortprefix:}},
which is slightly confusing, and a lot of code is now slightly
misleading or poorly named. But it should all work fine.
Also, while I was at it, I made updateCollation.php work as a transition
script, so you can apply the SQL patch and then run updateCollation.php
and things will work. However, with the new schema it's not trivial to
reverse this -- you'd have to recover the raw sort keys with some PHP.
Conversion goes at about a thousand rows a second for me, and seems to
be CPU-bound. Could probably be optimized.
I also adjusted the transition script so it will fix rows with collation
versions *greater* than the current one, as well as less. Thus if some
site wants to use their own collation, they can call it 137 or
something, and if they later want to switch back to MediaWiki stock
collation 7, it will work.
Also fixed a silly bug in updateCollation.php where it would say "1000
done" if it did nothing, and changed $res->numRows() >= self::BATCH_SIZE
to == so people don't wonder how it could be bigger (since it can't, I
hope).
2010-07-26 19:27:13 +00:00
* @ return string
*/
2010-07-26 22:04:19 +00:00
public function getCategorySortkey ( $prefix = '' ) {
2010-08-03 20:50:31 +00:00
$unprefixed = $this -> getText ();
2011-07-06 00:38:40 +00:00
// Anything that uses this hook should only depend
// on the Title object passed in, and should probably
// tell the users to run updateCollations.php --force
// in order to re-sort existing category relations.
wfRunHooks ( 'GetDefaultSortkey' , array ( $this , & $unprefixed ) );
2010-07-26 22:04:19 +00:00
if ( $prefix !== '' ) {
2011-01-28 22:27:52 +00:00
# Separate with a line feed, so the unprefixed part is only used as
# a tiebreaker when two pages have the exact same prefix.
# In UCA, tab is the only character that can sort above LF
# so we strip both of them from the original prefix.
2011-01-20 05:18:39 +00:00
$prefix = strtr ( $prefix , " \n \t " , ' ' );
2011-01-17 14:02:22 +00:00
return " $prefix\n $unprefixed " ;
Reconcept cl_raw_sortkey as cl_sortkey_prefix
In response to feedback by Phillipe Verdy on bug 164. Now if a bunch of
pages have [[Category:Foo| ]], they'll sort amongst themselves according
to page name, instead of in basically random order as it is currently.
This also makes storage more elegant and intuitive: instead of giving
NULL a magic meaning when there's no custom sortkey specified, we just
store an empty string, since there's no prefix.
This means {{defaultsort:}} really now means {{defaultsortprefix:}},
which is slightly confusing, and a lot of code is now slightly
misleading or poorly named. But it should all work fine.
Also, while I was at it, I made updateCollation.php work as a transition
script, so you can apply the SQL patch and then run updateCollation.php
and things will work. However, with the new schema it's not trivial to
reverse this -- you'd have to recover the raw sort keys with some PHP.
Conversion goes at about a thousand rows a second for me, and seems to
be CPU-bound. Could probably be optimized.
I also adjusted the transition script so it will fix rows with collation
versions *greater* than the current one, as well as less. Thus if some
site wants to use their own collation, they can call it 137 or
something, and if they later want to switch back to MediaWiki stock
collation 7, it will work.
Also fixed a silly bug in updateCollation.php where it would say "1000
done" if it did nothing, and changed $res->numRows() >= self::BATCH_SIZE
to == so people don't wonder how it could be bigger (since it can't, I
hope).
2010-07-26 19:27:13 +00:00
}
2010-07-26 22:04:19 +00:00
return $unprefixed ;
Reconcept cl_raw_sortkey as cl_sortkey_prefix
In response to feedback by Phillipe Verdy on bug 164. Now if a bunch of
pages have [[Category:Foo| ]], they'll sort amongst themselves according
to page name, instead of in basically random order as it is currently.
This also makes storage more elegant and intuitive: instead of giving
NULL a magic meaning when there's no custom sortkey specified, we just
store an empty string, since there's no prefix.
This means {{defaultsort:}} really now means {{defaultsortprefix:}},
which is slightly confusing, and a lot of code is now slightly
misleading or poorly named. But it should all work fine.
Also, while I was at it, I made updateCollation.php work as a transition
script, so you can apply the SQL patch and then run updateCollation.php
and things will work. However, with the new schema it's not trivial to
reverse this -- you'd have to recover the raw sort keys with some PHP.
Conversion goes at about a thousand rows a second for me, and seems to
be CPU-bound. Could probably be optimized.
I also adjusted the transition script so it will fix rows with collation
versions *greater* than the current one, as well as less. Thus if some
site wants to use their own collation, they can call it 137 or
something, and if they later want to switch back to MediaWiki stock
collation 7, it will work.
Also fixed a silly bug in updateCollation.php where it would say "1000
done" if it did nothing, and changed $res->numRows() >= self::BATCH_SIZE
to == so people don't wonder how it could be bigger (since it can't, I
hope).
2010-07-26 19:27:13 +00:00
}
2011-06-18 14:49:01 +00:00
/**
2011-06-24 22:10:39 +00:00
* Get the language in which the content of this page is written .
* Defaults to $wgContLang , but in certain cases it can be e . g .
* $wgLang ( such as special pages , which are in the user language ) .
2011-06-18 14:49:01 +00:00
*
2011-08-11 14:52:57 +00:00
* @ since 1.18
2011-06-18 14:49:01 +00:00
* @ return object Language
*/
public function getPageLanguage () {
2011-06-24 22:10:39 +00:00
global $wgLang ;
2011-11-02 20:55:08 +00:00
if ( $this -> isSpecialPage () ) {
2011-06-24 22:10:39 +00:00
// special pages are in the user language
return $wgLang ;
} elseif ( $this -> isCssOrJsPage () ) {
2011-06-18 14:49:01 +00:00
// css/js should always be LTR and is, in fact, English
2011-06-24 22:10:39 +00:00
return wfGetLangObj ( 'en' );
2011-06-18 14:49:01 +00:00
} elseif ( $this -> getNamespace () == NS_MEDIAWIKI ) {
// Parse mediawiki messages with correct target language
list ( /* $unused */ , $lang ) = MessageCache :: singleton () -> figureMessage ( $this -> getText () );
2011-06-24 22:10:39 +00:00
return wfGetLangObj ( $lang );
2011-06-18 14:49:01 +00:00
}
2011-06-24 22:10:39 +00:00
global $wgContLang ;
// If nothing special, it should be in the wiki content language
$pageLang = $wgContLang ;
// Hook at the end because we don't want to override the above stuff
wfRunHooks ( 'PageContentLanguage' , array ( $this , & $pageLang , $wgLang ) );
return wfGetLangObj ( $pageLang );
2011-06-18 14:49:01 +00:00
}
2011-10-02 16:01:35 +00:00
}