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
- var $mCounter = -1; // /< Number of times this page has been viewed (-1 means "not loaded")
+ private $mEstimateRevisions; // /< Estimated number of revisions; null of not loaded
var $mRestrictions = array(); // /< Array of groups allowed to edit this article
var $mOldRestrictions = false;
var $mCascadeRestriction; ///< Cascade restrictions on this page to included templates and images?
* Load Title object fields from a DB row.
* If false is given, the title will be treated as non-existing.
*
- * @param $row Object|false database row
- * @return void
+ * @param $row Object|bool database row
*/
public function loadFromRow( $row ) {
if ( $row ) { // page found
if ( isset( $row->page_is_redirect ) )
$this->mRedirect = (bool)$row->page_is_redirect;
if ( isset( $row->page_latest ) )
- $this->mLatestID = (int)$row->page_latest;
- if ( isset( $row->page_counter ) )
- $this->mCounter = (int)$row->page_counter;
+ $this->mLatestID = (int)$row->page_latest; # FIXME: whene3ver page_latest is updated, also update page_content_model
+ if ( isset( $row->page_content_model ) )
+ $this->mContentModelName = $row->page_content_model;
+ else
+ $this->mContentModelName = null; # initialized lazily in getContentModelName()
} else { // page not found
$this->mArticleID = 0;
$this->mLength = 0;
$this->mRedirect = false;
$this->mLatestID = 0;
- $this->mCounter = 0;
+ $this->mContentModelName = null; # initialized lazily in getContentModelName()
}
}
$t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
$t->mUrlform = wfUrlencode( $t->mDbkeyform );
$t->mTextform = str_replace( '_', ' ', $title );
+ $t->mContentModelName = null; # initialized lazily in getContentModelName()
return $t;
}
* @return Title the new object, or NULL on an error
*/
public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
+ if ( !MWNamespace::exists( $ns ) ) {
+ return null;
+ }
+
$t = new Title();
$t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki );
if ( $t->secureAndSplit() ) {
return $wgLegalTitleChars;
}
+ /**
+ * 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;
+ }
+
/**
* Get a string representation of a title suitable for
* including in a search index
return Sanitizer::escapeId( $fragment, 'noninitial' );
}
+ /**
+ * 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();
+ }
+ }
+
/**
* Determine whether the object refers to a page within
* this project.
return $this->mDbkeyform;
}
+ /**
+ * Get the DB key with the initial letter case as specified by the user
+ *
+ * @return String DB key
+ */
+ function getUserCaseDBKey() {
+ return $this->mUserCaseDBKey;
+ }
+
/**
* Get the namespace index, i.e. one of the NS_xxxx constants.
*
return $this->mNamespace;
}
+ /**
+ * Get the page's content model name
+ *
+ * @return Integer: Namespace index
+ */
+ public function getContentModelName() {
+ if ( empty( $this->mContentModelName ) ) {
+ $this->mContentModelName = ContentHandler::getDefaultModelFor( $this );
+ }
+
+ return $this->mContentModelName;
+ }
+
+ /**
+ * Conveniance method for checking a title's content model name
+ *
+ * @param $name
+ * @return true if $this->getContentModelName() == $name
+ */
+ public function hasContentModel( $name ) {
+ return $this->getContentModelName() == $name;
+ }
+
/**
* Get the namespace text
*
}
}
- // Strip off subpages
- $pagename = $this->getText();
- if ( strpos( $pagename, '/' ) !== false ) {
- list( $username , ) = explode( '/', $pagename, 2 );
- } else {
- $username = $pagename;
- }
-
if ( $wgContLang->needsGenderDistinction() &&
MWNamespace::hasGenderDistinction( $this->mNamespace ) ) {
- $gender = GenderCache::singleton()->getGenderOf( $username, __METHOD__ );
+ $gender = GenderCache::singleton()->getGenderOf( $this->getText(), __METHOD__ );
return $wgContLang->getGenderNsText( $this->mNamespace, $gender );
}
return $wgContLang->getNsText( $this->mNamespace );
}
- /**
- * Get the DB key with the initial letter case as specified by the user
- *
- * @return String DB key
- */
- function getUserCaseDBKey() {
- return $this->mUserCaseDBKey;
- }
-
/**
* Get the namespace text of the subject (rather than talk) page
*
}
/**
- * Get the Title fragment (i.e.\ the bit after the #) in text form
+ * Is this in a namespace that allows actual pages?
*
- * @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
+ * @return Bool
+ * @internal note -- uses hardcoded namespace index instead of constants
*/
- public function getFragmentForURL() {
- if ( $this->mFragment == '' ) {
- return '';
- } else {
- return '#' . Title::escapeFragmentForURL( $this->mFragment );
- }
+ public function canExist() {
+ return $this->mNamespace >= NS_MAIN;
}
/**
- * Get the default namespace index, for when there is no namespace
+ * Can this title be added to a user's watchlist?
*
- * @return Int Default namespace index
+ * @return Bool TRUE or FALSE
*/
- public function getDefaultNamespace() {
- return $this->mDefaultNamespace;
+ public function isWatchable() {
+ return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() );
}
/**
- * Get title for search index
+ * Returns true if this is a special page.
*
- * @return String a stripped-down title string ready for the
- * search index
+ * @return boolean
*/
- public function getIndexTitle() {
- return Title::indexTitle( $this->mNamespace, $this->mTextform );
+ public function isSpecialPage() {
+ return $this->getNamespace() == NS_SPECIAL;
}
/**
- * Get the prefixed database key form
+ * Returns true if this title resolves to the named special page
*
- * @return String the prefixed title, with underscores and
- * any interwiki and namespace prefixes
+ * @param $name String The special page name
+ * @return boolean
*/
- public function getPrefixedDBkey() {
- $s = $this->prefix( $this->mDbkeyform );
- $s = str_replace( ' ', '_', $s );
- return $s;
+ public function isSpecial( $name ) {
+ if ( $this->isSpecialPage() ) {
+ list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
+ if ( $name == $thisName ) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Get the prefixed title with spaces.
- * This is the form usually used for display
+ * 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
*
- * @return String the prefixed title, with spaces
+ * @return Title
*/
- 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;
+ 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 );
+ }
+ }
}
- return $this->mPrefixedText;
+ return $this;
}
/**
- /**
- * Get the prefixed title with spaces, plus any fragment
- * (part beginning with '#')
- *
- * @return String the prefixed title, with spaces and the fragment, including '#'
+ * 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
*/
- public function getFullText() {
- $text = $this->getPrefixedText();
- if ( $this->mFragment != '' ) {
- $text .= '#' . $this->mFragment;
- }
- return $text;
+ public function inNamespace( $ns ) {
+ return MWNamespace::equals( $this->getNamespace(), $ns );
}
/**
- * Get the base page name, i.e. the leftmost part before any slashes
+ * Returns true if the title is inside one of the specified namespaces.
*
- * @return String Base name
+ * @param ...$namespaces The namespaces to check for
+ * @return bool
+ * @since 1.19
*/
- public function getBaseText() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
- return $this->getText();
+ public function inNamespaces( /* ... */ ) {
+ $namespaces = func_get_args();
+ if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
+ $namespaces = $namespaces[0];
}
- $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] );
+ foreach ( $namespaces as $ns ) {
+ if ( $this->inNamespace( $ns ) ) {
+ return true;
+ }
}
- return implode( '/', $parts );
+
+ return false;
}
/**
- * Get the lowest-level subpage name, i.e. the rightmost part after any slashes
+ * 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.
*
- * @return String Subpage name
+ * 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
+ * @return bool
*/
- public function getSubpageText() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
- return( $this->mTextform );
- }
- $parts = explode( '/', $this->mTextform );
- return( $parts[count( $parts ) - 1] );
+ public function hasSubjectNamespace( $ns ) {
+ return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
}
/**
- * Get the HTML-escaped displayable text form.
- * Used for the title field in <a> tags.
+ * Is this Title in a namespace which contains content?
+ * In other words, is this a content page, for the purposes of calculating
+ * statistics, etc?
*
- * @return String the text, including any prefixes
+ * @return Boolean
*/
- public function getEscapedText() {
- return htmlspecialchars( $this->getPrefixedText() );
+ public function isContentPage() {
+ return MWNamespace::isContent( $this->getNamespace() );
}
/**
- * Get a URL-encoded form of the subpage text
+ * Would anybody with sufficient privileges be able to move this page?
+ * Some pages just aren't movable.
*
- * @return String URL-encoded subpage name
+ * @return Bool TRUE or FALSE
*/
- public function getSubpageUrlForm() {
- $text = $this->getSubpageText();
- $text = wfUrlencode( str_replace( ' ', '_', $text ) );
- return( $text );
+ 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;
}
/**
- * Get a URL-encoded title (not an actual URL) including interwiki
+ * 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();
*
- * @return String the URL-encoded form
+ * @since 1.18
+ * @return Bool
*/
- public function getPrefixedURL() {
- $s = $this->prefix( $this->mDbkeyform );
- $s = wfUrlencode( str_replace( ' ', '_', $s ) );
- return $s;
+ public function isMainPage() {
+ return $this->equals( Title::newMainPage() );
}
/**
- * Get a real URL referring to this title, with interwiki link and
- * fragment
+ * Is this a subpage?
*
- * @param $query \twotypes{\string,\array} an optional query string, 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).
- * @param $variant String language variant of url (for sr, zh..)
- * @return String the URL
+ * @return Bool
+ */
+ 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() {
+ return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
+ }
+
+ /**
+ * Could this page contain custom CSS or JavaScript for the global UI.
+ * This is generally true for pages in the MediaWiki namespace having CONTENT_MODEL_CSS
+ * or CONTENT_MODEL_JAVASCRIPT.
+ *
+ * This method does *not* return true for per-user JS/CSS. Use isCssJsSubpage() for that!
+ *
+ * Note that this method should not return true for pages that contain and show "inactive" CSS or JS.
+ *
+ * @return Bool
+ */
+ public function isCssOrJsPage() {
+ $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
+ && ( $this->hasContentModel( CONTENT_MODEL_CSS )
+ || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) );
+
+ #NOTE: this hook is also called in ContentHandler::getDefaultModel. It's called here again to make sure
+ # hook funktions can force this method to return true even outside the mediawiki namespace.
+
+ wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$isCssOrJsPage ) );
+
+ return $isCssOrJsPage;
+ }
+
+ /**
+ * Is this a .css or .js subpage of a user page?
+ * @return Bool
+ */
+ public function isCssJsSubpage() {
+ return ( NS_USER == $this->mNamespace && $this->isSubpage()
+ && ( $this->hasContentModel( CONTENT_MODEL_CSS )
+ || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
+ }
+
+ /**
+ * 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 && $this->isSubpage()
+ && $this->hasContentModel( CONTENT_MODEL_CSS ) );
+ }
+
+ /**
+ * Is this a .js subpage of a user page?
+ *
+ * @return Bool
+ */
+ public function isJsSubpage() {
+ return ( NS_USER == $this->mNamespace && $this->isSubpage()
+ && $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) );
+ }
+
+ /**
+ * 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() {
+ wfDeprecated( __METHOD__, '1.19' );
+ 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;
+ }
+
+ /**
+ * Helper to fix up the get{Local,Full,Link,Canonical}URL args
+ * 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)
+ * @return String
+ */
+ private static function fixUrlQueryArgs( $query, $query2 = false ) {
+ if( $query2 !== false ) {
+ 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" );
+ }
+ 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;
+ }
+
+ /**
+ * Get a real URL referring to this title, with interwiki link and
+ * fragment
+ *
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
+ * @return String the URL
*/
- public function getFullURL( $query = '', $variant = false ) {
+ public function getFullURL( $query = '', $query2 = false ) {
+ $query = self::fixUrlQueryArgs( $query, $query2 );
+
# Hand off all the decisions on urls to getLocalURL
- $url = $this->getLocalURL( $query, $variant );
+ $url = $this->getLocalURL( $query );
# 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
# Finally, add the fragment.
$url .= $this->getFragmentForURL();
- wfRunHooks( 'GetFullURL', array( &$this, &$url, $query, $variant ) );
+ wfRunHooks( 'GetFullURL', array( &$this, &$url, $query ) );
return $url;
}
* Get a URL with no fragment or server name. If this page is generated
* with action=render, $wgServer is prepended.
*
- * @param $query Mixed: an optional query string; if not specified,
- * $wgArticlePath will be used. Can be specified as an associative array
- * as well, e.g., array( 'action' => 'edit' ) (keys and values will be
- * URL-escaped).
- * @param $variant String language variant of url (for sr, zh..)
+
+ * @param $query string|array an optional query string,
+ * 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
+ * The second parameter is deprecated since 1.19. Pass it as a key,value
+ * pair in the first parameter array instead.
+ *
* @return String the URL
*/
- public function getLocalURL( $query = '', $variant = false ) {
+ public function getLocalURL( $query = '', $query2 = false ) {
global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
- global $wgVariantArticlePath;
- if ( is_array( $query ) ) {
- $query = wfArrayToCGI( $query );
- }
+ $query = self::fixUrlQueryArgs( $query, $query2 );
$interwiki = Interwiki::fetch( $this->mInterwiki );
if ( $interwiki ) {
} else {
$dbkey = wfUrlencode( $this->getPrefixedDBkey() );
if ( $query == '' ) {
- if ( $variant != false && $this->getPageLanguage()->hasVariants() ) {
- if ( !$wgVariantArticlePath ) {
- $variantArticlePath = "$wgScript?title=$1&variant=$2"; // default
- } else {
- $variantArticlePath = $wgVariantArticlePath;
- }
- $url = str_replace( '$2', urlencode( $variant ), $variantArticlePath );
- $url = str_replace( '$1', $dbkey, $url );
- } else {
- $url = str_replace( '$1', $dbkey, $wgArticlePath );
- wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) );
- }
+ $url = str_replace( '$1', $dbkey, $wgArticlePath );
+ wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) );
} else {
- global $wgActionPaths;
+ global $wgVariantArticlePath, $wgActionPaths;
$url = false;
$matches = array();
+
if ( !empty( $wgActionPaths ) &&
preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches ) )
{
}
}
+ 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 );
+ }
+ }
+
if ( $url === false ) {
if ( $query == '-' ) {
$query = '';
}
}
- wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query, $variant ) );
+ wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query ) );
// @todo FIXME: This causes breakage in various places when we
// actually expected a local URL and end up with dupe prefixes.
$url = $wgServer . $url;
}
}
- wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query, $variant ) );
+ wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query ) );
return $url;
}
* The result obviously should not be URL-escaped, but does need to be
* HTML-escaped if it's being output in HTML.
*
- * @param $query Array of Strings An associative array of key => value pairs for the
- * query string. Keys and values will be escaped.
- * @param $variant String language variant of URL (for sr, zh..). Ignored
- * for external links. Default is "false" (same variant as current page,
- * for anonymous users).
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @return String the URL
*/
- public function getLinkURL( $query = array(), $variant = false ) {
+ public function getLinkURL( $query = '', $query2 = false ) {
wfProfileIn( __METHOD__ );
if ( $this->isExternal() ) {
- $ret = $this->getFullURL( $query );
+ $ret = $this->getFullURL( $query, $query2 );
} elseif ( $this->getPrefixedText() === '' && $this->getFragment() !== '' ) {
$ret = $this->getFragmentForURL();
} else {
- $ret = $this->getLocalURL( $query, $variant ) . $this->getFragmentForURL();
+ $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL();
}
wfProfileOut( __METHOD__ );
return $ret;
* Get an HTML-escaped version of the URL form, suitable for
* using in a link, without a server name or fragment
*
- * @param $query String an optional query string
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @return String the URL
*/
- public function escapeLocalURL( $query = '' ) {
- return htmlspecialchars( $this->getLocalURL( $query ) );
+ public function escapeLocalURL( $query = '', $query2 = false ) {
+ wfDeprecated( __METHOD__, '1.19' );
+ return htmlspecialchars( $this->getLocalURL( $query, $query2 ) );
}
/**
* Get an HTML-escaped version of the URL form, suitable for
* using in a link, including the server name and fragment
*
- * @param $query String an optional query string
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @return String the URL
*/
- public function escapeFullURL( $query = '' ) {
- return htmlspecialchars( $this->getFullURL( $query ) );
+ public function escapeFullURL( $query = '', $query2 = false ) {
+ wfDeprecated( __METHOD__, '1.19' );
+ return htmlspecialchars( $this->getFullURL( $query, $query2 ) );
}
/**
* if $wgInternalServer is not set. If the server variable used is
* protocol-relative, the URL will be expanded to http://
*
- * @param $query String an optional query string
- * @param $variant String language variant of url (for sr, zh..)
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @return String the URL
*/
- public function getInternalURL( $query = '', $variant = false ) {
+ public function getInternalURL( $query = '', $query2 = false ) {
global $wgInternalServer, $wgServer;
+ $query = self::fixUrlQueryArgs( $query, $query2 );
$server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
- $url = wfExpandUrl( $server . $this->getLocalURL( $query, $variant ), PROTO_HTTP );
- wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query, $variant ) );
+ $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP );
+ wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) );
return $url;
}
*
* NOTE: Unlike getInternalURL(), the canonical URL includes the fragment
*
- * @param $query string An optional query string
- * @param $variant string Language variant of URL (for sr, zh, ...)
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @return string The URL
* @since 1.18
*/
- public function getCanonicalURL( $query = '', $variant = false ) {
- $url = wfExpandUrl( $this->getLocalURL( $query, $variant ) . $this->getFragmentForURL(), PROTO_CANONICAL );
- wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query, $variant ) );
+ public function getCanonicalURL( $query = '', $query2 = false ) {
+ $query = self::fixUrlQueryArgs( $query, $query2 );
+ $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL );
+ wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query ) );
return $url;
}
/**
* HTML-escaped version of getCanonicalURL()
+ *
+ * See getLocalURL for the arguments.
+ *
+ * @see self::getLocalURL
* @since 1.18
+ * @return string
*/
- public function escapeCanonicalURL( $query = '', $variant = false ) {
- return htmlspecialchars( $this->getCanonicalURL( $query, $variant ) );
+ public function escapeCanonicalURL( $query = '', $query2 = false ) {
+ wfDeprecated( __METHOD__, '1.19' );
+ return htmlspecialchars( $this->getCanonicalURL( $query, $query2 ) );
}
/**
return $s;
}
- /**
- * Is this page "semi-protected" - the *only* protection is autoconfirm?
- *
- * @param $action String Action to check (default: edit)
- * @return Bool
- */
- 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;
- }
- }
-
- /**
- * Does the title correspond to a protected article?
- *
- * @param $action String the action the page is protected from,
- * by default checks all actions.
- * @return Bool
- */
- 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;
- }
-
- /**
- * Determines if $user is unable to edit this page because it has been protected
- * by $wgNamespaceProtection.
- *
- * @param $user User object to check permissions
- * @return Bool
- */
- 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;
- }
-
- /**
- * Is this a conversion table for the LanguageConverter?
- *
- * @return Bool
- */
- public function isConversionTable() {
- if(
- $this->getNamespace() == NS_MEDIAWIKI &&
- strpos( $this->getText(), 'Conversiontable' ) !== false
- )
- {
- return true;
- }
-
- return false;
- }
-
/**
* Is $wgUser watching this page?
*
* @todo fold these checks into userCan()
*/
public function userCanRead() {
+ wfDeprecated( __METHOD__, '1.19' );
return $this->userCan( 'read' );
}
/**
- * Can $wgUser perform $action on this page?
+ * Can $user perform $action on this page?
* This skips potentially expensive cascading permission checks
* as well as avoids expensive error formatting
*
* May provide false positives, but should never provide a false negative.
*
* @param $action String action that permission needs to be checked for
- * @param $user User to check (since 1.19)
+ * @param $user User to check (since 1.19); $wgUser will be used if not
+ * provided.
* @return Bool
*/
public function quickUserCan( $action, $user = null ) {
}
/**
- * Can $wgUser perform $action on this page?
+ * Can $user perform $action on this page?
*
* @param $action String action that permission needs to be checked for
- * @param $user User to check (since 1.19)
- * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries.
+ * @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.
* @return Bool
*/
public function userCan( $action, $user = null, $doExpensiveQueries = true ) {
*
* @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 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.
+ * @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.
* @return Array of arguments to wfMsg to explain permissions problems.
*/
public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) {
* @return Array list of errors
*/
private function checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
- $ns = $this->getNamespace();
-
if ( $action == 'create' ) {
- if ( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk', $ns ) ) ||
- ( !$this->isTalkPage() && !$user->isAllowed( 'createpage', $ns ) ) ) {
+ if ( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
+ ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) ) {
$errors[] = $user->isAnon() ? array( 'nocreatetext' ) : array( 'nocreate-loggedin' );
}
} elseif ( $action == 'move' ) {
- if ( !$user->isAllowed( 'move-rootuserpages', $ns )
- && $ns == NS_USER && !$this->isSubpage() ) {
+ if ( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
// Show user page-specific message only if the user can move other pages
$errors[] = array( 'cant-move-user-page' );
}
// Check if user is allowed to move files if it's a file
- if ( $ns == NS_FILE && !$user->isAllowed( 'movefile', $ns ) ) {
+ if ( $this->mNamespace == NS_FILE && !$user->isAllowed( 'movefile' ) ) {
$errors[] = array( 'movenotallowedfile' );
}
- if ( !$user->isAllowed( 'move', $ns) ) {
+ if ( !$user->isAllowed( 'move' ) ) {
// User can't move anything
-
- $userCanMove = in_array( 'move', User::getGroupPermissions(
- array( 'user' ), $ns ), true );
- $autoconfirmedCanMove = in_array( 'move', User::getGroupPermissions(
- array( 'autoconfirmed' ), $ns ), true );
-
+ global $wgGroupPermissions;
+ $userCanMove = false;
+ if ( isset( $wgGroupPermissions['user']['move'] ) ) {
+ $userCanMove = $wgGroupPermissions['user']['move'];
+ }
+ $autoconfirmedCanMove = false;
+ if ( isset( $wgGroupPermissions['autoconfirmed']['move'] ) ) {
+ $autoconfirmedCanMove = $wgGroupPermissions['autoconfirmed']['move'];
+ }
if ( $user->isAnon() && ( $userCanMove || $autoconfirmedCanMove ) ) {
// custom message if logged-in users without any special rights can move
$errors[] = array( 'movenologintext' );
}
}
} elseif ( $action == 'move-target' ) {
- if ( !$user->isAllowed( 'move', $ns ) ) {
+ if ( !$user->isAllowed( 'move' ) ) {
// User can't move anything
$errors[] = array( 'movenotallowed' );
- } elseif ( !$user->isAllowed( 'move-rootuserpages', $ns )
- && $ns == NS_USER && !$this->isSubpage() ) {
+ } elseif ( !$user->isAllowed( 'move-rootuserpages' )
+ && $this->mNamespace == NS_USER && !$this->isSubpage() ) {
// Show user page-specific message only if the user can move other pages
$errors[] = array( 'cant-move-to-user-page' );
}
- } elseif ( !$user->isAllowed( $action, $ns ) ) {
+ } elseif ( !$user->isAllowed( $action ) ) {
$errors[] = $this->missingPermissionError( $action, $short );
}
if ( $right == 'sysop' ) {
$right = 'protect';
}
- if ( $right != '' && !$user->isAllowed( $right, $this->mNamespace ) ) {
+ if ( $right != '' && !$user->isAllowed( $right ) ) {
// Users with 'editprotected' permission can edit protected pages
// without cascading option turned on.
- if ( $action != 'edit' || !$user->isAllowed( 'editprotected', $this->mNamespace )
+ if ( $action != 'edit' || !$user->isAllowed( 'editprotected' )
|| $this->mCascadeRestriction )
{
$errors[] = array( 'protectedpagetext', $right );
if ( isset( $restrictions[$action] ) ) {
foreach ( $restrictions[$action] as $right ) {
$right = ( $right == 'sysop' ) ? 'protect' : $right;
- if ( $right != '' && !$user->isAllowed( $right, $this->mNamespace ) ) {
+ if ( $right != '' && !$user->isAllowed( $right ) ) {
$pages = '';
foreach ( $cascadingSources as $page )
$pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
* @return Array list of errors
*/
private function checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
+ global $wgDeleteRevisionsLimit, $wgLang;
+
if ( $action == 'protect' ) {
if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $doExpensiveQueries, true ) ) ) {
// If they can't edit, they shouldn't protect.
$title_protection['pt_create_perm'] = 'protect'; // B/C
}
if( $title_protection['pt_create_perm'] == '' ||
- !$user->isAllowed( $title_protection['pt_create_perm'],
- $this->mNamespace ) ) {
+ !$user->isAllowed( $title_protection['pt_create_perm'] ) )
+ {
$errors[] = array( 'titleprotected', User::whoIs( $title_protection['pt_user'] ), $title_protection['pt_reason'] );
}
}
} elseif ( !$this->isMovable() ) {
$errors[] = array( 'immobile-target-page' );
}
+ } elseif ( $action == 'delete' ) {
+ if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
+ && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion() )
+ {
+ $errors[] = array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) );
+ }
}
return $errors;
}
// Don't block the user from editing their own talk page unless they've been
// explicitly blocked from that too.
} elseif( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) {
- $block = $user->mBlock;
+ $block = $user->getBlock();
// This is from OutputPage::blockedPage
// Copied at r23888 by werdna
$link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]";
$blockid = $block->getId();
- $blockExpiry = $user->mBlock->mExpiry;
- $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $user->mBlock->mTimestamp ), true );
+ $blockExpiry = $block->getExpiry();
+ $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $block->mTimestamp ), true );
if ( $blockExpiry == 'infinity' ) {
$blockExpiry = wfMessage( 'infiniteblock' )->text();
} else {
$blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true );
}
- $intended = strval( $user->mBlock->getTarget() );
+ $intended = strval( $block->getTarget() );
$errors[] = array( ( $block->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $ip, $name,
$blockid, $blockExpiry, $intended, $blockTimestamp );
* @return Array list of errors
*/
private function checkReadPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) {
+ global $wgWhitelistRead, $wgGroupPermissions, $wgRevokePermissions;
static $useShortcut = null;
# Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below
if ( is_null( $useShortcut ) ) {
- global $wgGroupPermissions, $wgRevokePermissions;
$useShortcut = true;
if ( empty( $wgGroupPermissions['*']['read'] ) ) {
# Not a public wiki, so no shortcut
}
}
- # Shortcut for public wikis, allows skipping quite a bit of code
+ $whitelisted = false;
if ( $useShortcut ) {
- return $errors;
- }
-
- # If the user is allowed to read pages, he is allowed to read all pages
- if ( $user->isAllowed( 'read', $this->mNamespace ) ) {
- return $errors;
- }
-
- # Always grant access to the login page.
- # Even anons need to be able to log in.
- if ( $this->isSpecial( 'Userlogin' )
+ # 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' )
|| $this->isSpecial( 'ChangePassword' )
|| $this->isSpecial( 'PasswordReset' )
) {
- return $errors;
- }
-
- # Time to check the whitelist
- global $wgWhitelistRead;
-
- # Only to these checks is there's something to check against
- if ( is_array( $wgWhitelistRead ) && count( $wgWhitelistRead ) ) {
- # Check for explicit whitelisting
+ # 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
$name = $this->getPrefixedText();
$dbName = $this->getPrefixedDBKey();
- // Check with and without underscores
+ // Check for explicit whitelisting with and without underscores
if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) {
- return $errors;
- }
-
- # Old settings might have the title prefixed with
- # a colon for main-namespace pages
- if ( $this->getNamespace() == NS_MAIN ) {
+ $whitelisted = true;
+ } elseif ( $this->getNamespace() == NS_MAIN ) {
+ # Old settings might have the title prefixed with
+ # a colon for main-namespace pages
if ( in_array( ':' . $name, $wgWhitelistRead ) ) {
- return $errors;
+ $whitelisted = true;
}
- }
-
- # If it's a special page, ditch the subpage bit and check again
- if ( $this->isSpecialPage() ) {
+ } elseif ( $this->isSpecialPage() ) {
+ # If it's a special page, ditch the subpage bit and check again
$name = $this->getDBkey();
list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
if ( $name !== false ) {
$pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
if ( in_array( $pure, $wgWhitelistRead, true ) ) {
- return $errors;
+ $whitelisted = true;
}
}
}
}
- $errors[] = $this->missingPermissionError( $action, $short );
+ 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 );
+ }
+ }
+
return $errors;
}
}
$groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
- User::getGroupsWithPermission( $action, $this->mNamespace ) );
+ User::getGroupsWithPermission( $action ) );
if ( count( $groups ) ) {
global $wgLang;
$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;
- }
-
- /**
- * Is this title subject to title protection?
- * Title protection is the one applied against creation of such title.
- *
- * @return Mixed An associative array representing any existent title
- * protection, or false if there's none.
- */
- private function getTitleProtection() {
- // Can't protect pages in special namespaces
- if ( $this->getNamespace() < 0 ) {
- return false;
- }
-
- // Can't protect pages that exist.
- if ( $this->exists() ) {
- return false;
- }
-
- if ( !isset( $this->mTitleProtection ) ) {
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( 'protected_titles', '*',
- array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
- __METHOD__ );
-
- // fetchRow returns false if there are no rows.
- $this->mTitleProtection = $dbr->fetchRow( $res );
- }
- return $this->mTitleProtection;
- }
-
- /**
- * Update the title protection status
- *
- * @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 ) {
- global $wgUser, $wgContLang;
-
- if ( $create_perm == implode( ',', $this->getRestrictions( 'create' ) )
- && $expiry == $this->mRestrictionsExpiry['create'] ) {
- // No change
- return true;
- }
-
- list ( $namespace, $title ) = array( $this->getNamespace(), $this->getDBkey() );
-
- $dbw = wfGetDB( DB_MASTER );
-
- $encodedExpiry = $dbw->encodeExpiry( $expiry );
-
- $expiry_description = '';
- if ( $encodedExpiry != $dbw->getInfinity() ) {
- $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ),
- $wgContLang->date( $expiry ) , $wgContLang->time( $expiry ) ) . ')';
- } else {
- $expiry_description .= ' (' . wfMsgForContent( 'protect-expiry-indefinite' ) . ')';
- }
-
- # Update protection table
- if ( $create_perm != '' ) {
- $this->mTitleProtection = array(
- 'pt_namespace' => $namespace,
- 'pt_title' => $title,
- 'pt_create_perm' => $create_perm,
- 'pt_timestamp' => $dbw->encodeExpiry( wfTimestampNow() ),
- 'pt_expiry' => $encodedExpiry,
- 'pt_user' => $wgUser->getId(),
- 'pt_reason' => $reason,
- );
- $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ),
- $this->mTitleProtection, __METHOD__ );
- } else {
- $dbw->delete( 'protected_titles', array( 'pt_namespace' => $namespace,
- 'pt_title' => $title ), __METHOD__ );
- $this->mTitleProtection = false;
- }
-
- # Update the protection log
- if ( $dbw->affectedRows() ) {
- $log = new LogPage( 'protect' );
-
- if ( $create_perm ) {
- $params = array( "[create=$create_perm] $expiry_description", '' );
- $log->addEntry( ( isset( $this->mRestrictions['create'] ) && $this->mRestrictions['create'] ) ? 'modify' : 'protect', $this, trim( $reason ), $params );
- } else {
- $log->addEntry( 'unprotect', $this, $reason );
- }
- }
-
- return true;
- }
-
- /**
- * Remove any title protection due to page existing
- */
- public function deleteTitleProtection() {
- $dbw = wfGetDB( DB_MASTER );
-
- $dbw->delete(
- 'protected_titles',
- array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
- __METHOD__
- );
- $this->mTitleProtection = false;
- }
-
- /**
- * Would anybody with sufficient privileges be able to move this page?
- * Some pages just aren't movable.
- *
- * @return Bool TRUE or FALSE
- */
- 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;
- }
-
- /**
- * 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();
- *
- * @since 1.18
- * @return Bool
- */
- public function isMainPage() {
- return $this->equals( Title::newMainPage() );
+ $method = array_shift( $checks );
+ $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $errors;
}
/**
- * Is this a talk page of some sort?
+ * 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 isTalkPage() {
- return MWNamespace::isTalk( $this->getNamespace() );
+ public function userCanEditCssSubpage() {
+ global $wgUser;
+ wfDeprecated( __METHOD__, '1.19' );
+ return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'editusercss' ) )
+ || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) );
}
/**
- * Is this a subpage?
+ * 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 isSubpage() {
- return MWNamespace::hasSubpages( $this->mNamespace )
- ? strpos( $this->getText(), '/' ) !== false
- : false;
+ public function userCanEditJsSubpage() {
+ global $wgUser;
+ wfDeprecated( __METHOD__, '1.19' );
+ return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'edituserjs' ) )
+ || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) );
}
/**
- * 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
+ * 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 function inNamespace( $ns ) {
- return MWNamespace::equals( $this->getNamespace(), $ns );
+ 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 true if the title is inside one of the specified namespaces.
+ * Returns restriction types for the current Title
*
- * @param ...$namespaces The namespaces to check for
- * @return bool
- * @since 1.19
+ * @return array applicable restriction types
*/
- public function inNamespaces( /* ... */ ) {
- $namespaces = func_get_args();
- if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) {
- $namespaces = $namespaces[0];
+ public function getRestrictionTypes() {
+ if ( $this->isSpecialPage() ) {
+ return array();
}
- foreach ( $namespaces as $ns ) {
- if ( $this->inNamespace( $ns ) ) {
- return true;
- }
+ $types = self::getFilteredRestrictionTypes( $this->exists() );
+
+ if ( $this->getNamespace() != NS_FILE ) {
+ # Remove the upload restriction for non-file titles
+ $types = array_diff( $types, array( 'upload' ) );
}
- return false;
- }
+ wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
- /**
- * 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.
- *
- * 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
- */
- public function hasSubjectNamespace( $ns ) {
- return MWNamespace::subjectEquals( $this->getNamespace(), $ns );
+ wfDebug( __METHOD__ . ': applicable restrictions to [[' .
+ $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
+
+ return $types;
}
/**
- * Does this have subpages? (Warning, usually requires an extra DB query.)
+ * Is this title subject to title protection?
+ * Title protection is the one applied against creation of such title.
*
- * @return Bool
+ * @return Mixed An associative array representing any existent title
+ * protection, or false if there's none.
*/
- public function hasSubpages() {
- if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
- # Duh
+ private function getTitleProtection() {
+ // Can't protect pages in special namespaces
+ if ( $this->getNamespace() < 0 ) {
return false;
}
- # 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;
+ // Can't protect pages that exist.
+ if ( $this->exists() ) {
+ return false;
}
- $subpages = $this->getSubpages( 1 );
- if ( $subpages instanceof TitleArray ) {
- return $this->mHasSubpages = (bool)$subpages->count();
+ if ( !isset( $this->mTitleProtection ) ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'protected_titles', '*',
+ array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
+ __METHOD__ );
+
+ // fetchRow returns false if there are no rows.
+ $this->mTitleProtection = $dbr->fetchRow( $res );
}
- return $this->mHasSubpages = false;
+ return $this->mTitleProtection;
}
/**
- * Get all subpages of this page.
+ * Update the title protection status
*
- * @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
+ * @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 getSubpages( $limit = -1 ) {
- if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
- return array();
- }
+ public function updateTitleProtection( $create_perm, $reason, $expiry ) {
+ wfDeprecated( __METHOD__, '1.19' );
- $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
- )
- );
- }
+ global $wgUser;
- /**
- * 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;
- }
+ $limit = array( 'create' => $create_perm );
+ $expiry = array( 'create' => $expiry );
- /**
- * 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;
- }
+ $page = WikiPage::factory( $this );
+ $status = $page->doUpdateRestrictions( $limit, $expiry, false, $reason, $wgUser );
- /**
- * 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 ) );
+ return $status->isOK();
}
/**
- * Is this a *valid* .css or .js subpage of a user page?
- *
- * @return Bool
- * @deprecated since 1.17
+ * Remove any title protection due to page existing
*/
- public function isValidCssJsSubpage() {
- return $this->isCssJsSubpage();
- }
+ public function deleteTitleProtection() {
+ $dbw = wfGetDB( DB_MASTER );
- /**
- * 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 );
+ $dbw->delete(
+ 'protected_titles',
+ array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
+ __METHOD__
+ );
+ $this->mTitleProtection = false;
}
/**
- * Is this a .css subpage of a user page?
+ * Is this page "semi-protected" - the *only* protection is autoconfirm?
*
+ * @param $action String Action to check (default: edit)
* @return Bool
*/
- public function isCssSubpage() {
- return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) );
+ 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;
+ }
}
/**
- * Is this a .js subpage of a user page?
+ * Does the title correspond to a protected article?
*
+ * @param $action String the action the page is protected from,
+ * by default checks all actions.
* @return Bool
*/
- public function isJsSubpage() {
- return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) );
- }
+ public function isProtected( $action = '' ) {
+ global $wgRestrictionLevels;
- /**
- * 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__ );
- return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'editusercss' ) )
- || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) );
+ $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;
}
/**
- * Protect js subpages of user pages: can $wgUser edit
- * this page?
+ * Determines if $user is unable to edit this page because it has been protected
+ * by $wgNamespaceProtection.
*
- * @deprecated in 1.19; will be removed in 1.20. Use getUserPermissionsErrors() instead.
+ * @param $user User object to check permissions
* @return Bool
*/
- public function userCanEditJsSubpage() {
- global $wgUser;
- wfDeprecated( __METHOD__ );
- return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'edituserjs' ) )
- || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) );
+ 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;
}
/**
return array( $sources, $pagerestrictions );
}
+ /**
+ * 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;
+ }
+
/**
* Returns cascading restrictions for the current article
*
if ( $oldFashionedRestrictions === null ) {
$oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
- array( 'page_id' => $this->getArticleId() ), __METHOD__ );
+ array( 'page_id' => $this->getArticleID() ), __METHOD__ );
}
if ( $oldFashionedRestrictions != '' ) {
$res = $dbr->select(
'page_restrictions',
'*',
- array( 'pr_page' => $this->getArticleId() ),
+ array( 'pr_page' => $this->getArticleID() ),
__METHOD__
);
}
}
+ /**
+ * 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;
+ }
+
/**
* Purge expired restrictions from the page_restrictions table
*/
}
/**
- * Accessor/initialisation for mRestrictions
+ * Does this have subpages? (Warning, usually requires an extra DB query.)
*
- * @param $action String action that permission needs to be checked for
- * @return Array of Strings the array of groups allowed to edit this article
+ * @return Bool
*/
- public function getRestrictions( $action ) {
- if ( !$this->mRestrictionsLoaded ) {
- $this->loadRestrictions();
+ public function hasSubpages() {
+ if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+ # Duh
+ return false;
}
- return isset( $this->mRestrictions[$action] )
- ? $this->mRestrictions[$action]
- : array();
+
+ # 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;
}
/**
- * Get the expiry time for the restriction against a given action
+ * Get all subpages of this page.
*
- * @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.
+ * @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
*/
- public function getRestrictionExpiry( $action ) {
- if ( !$this->mRestrictionsLoaded ) {
- $this->loadRestrictions();
+ public function getSubpages( $limit = -1 ) {
+ if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) {
+ return array();
}
- return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
+
+ $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
+ )
+ );
}
/**
return $deleted;
}
- /**
- * Get the number of views of this page
- *
- * @return int The view count for the page
- */
- public function getCount() {
- if ( $this->mCounter == -1 ) {
- if ( $this->exists() ) {
- $dbr = wfGetDB( DB_SLAVE );
- $this->mCounter = $dbr->selectField( 'page',
- 'page_counter',
- array( 'page_id' => $this->getArticleID() ),
- __METHOD__
- );
- } else {
- $this->mCounter = 0;
- }
- }
-
- return $this->mCounter;
- }
-
/**
* Get the article ID for this Title from the link cache,
* adding it if necessary
* This clears some fields in this object, and clears any associated
* keys in the "bad links" section of the link cache.
*
- * - This is called from Article::doEdit() and Article::insertOn() to allow
+ * - This is called from WikiPage::doEdit() and WikiPage::insertOn() to allow
* loading of the new page_id. It's also called from
- * Article::doDeleteArticle()
+ * WikiPage::doDeleteArticle()
*
* @param $newid Int the new Article ID
*/
$this->mRedirect = null;
$this->mLength = -1;
$this->mLatestID = false;
- $this->mCounter = -1;
- }
-
- /**
- * Updates page_touched for this page; called from LinksUpdate.php
- *
- * @return Bool true if the update succeded
- */
- 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;
- }
-
- /**
- * 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;
- }
-
- /**
- * 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...
- '[^' . Title::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;
+ $this->mEstimateRevisions = null;
}
/**
}
/**
- * 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 "#".
+ * Get an array of Title objects linking to this Title
+ * Also stores the IDs in the link cache.
*
- * Deprecated for public use, use Title::makeTitle() with fragment parameter.
- * Still in active use privately.
+ * WARNING: do not use this function on arbitrary user-supplied titles!
+ * On heavily-used templates it will max out the memory.
*
- * @param $fragment String text
+ * @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 setFragment( $fragment ) {
- $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) );
- }
+ public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
+ if ( count( $options ) > 0 ) {
+ $db = wfGetDB( DB_MASTER );
+ } else {
+ $db = wfGetDB( DB_SLAVE );
+ }
- /**
- * 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() );
+ $res = $db->select(
+ array( 'page', $table ),
+ array( 'page_namespace', 'page_title', 'page_id', 'page_len', 'page_is_redirect', 'page_latest' ),
+ array(
+ "{$prefix}_from=page_id",
+ "{$prefix}_namespace" => $this->getNamespace(),
+ "{$prefix}_title" => $this->getDBkey() ),
+ __METHOD__,
+ $options
+ );
+
+ $retVal = array();
+ if ( $res->numRows() ) {
+ $linkCache = LinkCache::singleton();
+ foreach ( $res as $row ) {
+ $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+ if ( $titleObj ) {
+ $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
+ $retVal[] = $titleObj;
+ }
+ }
+ }
+ return $retVal;
}
- /**
- * Get a title object associated with the subject page of this
- * talk page
+ /**
+ * Get an array of Title objects using this Title as a template
+ * Also stores the IDs in the link cache.
*
- * @return Title the object for the subject page
+ * 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 linking here
*/
- 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() );
+ public function getTemplateLinksTo( $options = array() ) {
+ return $this->getLinksTo( $options, 'templatelinks', 'tl' );
}
/**
- * Get an array of Title objects linking to this Title
+ * 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!
* @param $prefix String: fields prefix
* @return Array of Title objects linking here
*/
- public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
- $linkCache = LinkCache::singleton();
+ 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 );
$db = wfGetDB( DB_SLAVE );
}
+ $namespaceFiled = "{$prefix}_namespace";
+ $titleField = "{$prefix}_title";
+
$res = $db->select(
- array( 'page', $table ),
- array( 'page_namespace', 'page_title', 'page_id', 'page_len', 'page_is_redirect', 'page_latest' ),
- array(
- "{$prefix}_from=page_id",
- "{$prefix}_namespace" => $this->getNamespace(),
- "{$prefix}_title" => $this->getDBkey() ),
+ array( $table, 'page' ),
+ array( $namespaceFiled, $titleField, 'page_id', 'page_len', 'page_is_redirect', 'page_latest' ),
+ array( "{$prefix}_from" => $id ),
__METHOD__,
- $options
+ $options,
+ array( 'page' => array( 'LEFT JOIN', array( "page_namespace=$namespaceFiled", "page_title=$titleField" ) ) )
);
$retVal = array();
- if ( $db->numRows( $res ) ) {
+ if ( $res->numRows() ) {
+ $linkCache = LinkCache::singleton();
foreach ( $res as $row ) {
- $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
if ( $titleObj ) {
- $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
+ if ( $row->page_id ) {
+ $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
+ } else {
+ $linkCache->addBadLinkObj( $titleObj );
+ }
$retVal[] = $titleObj;
}
}
}
/**
- * Get an array of Title objects using this Title as a template
+ * 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 linking here
+ * @return Array of Title the Title objects used here
*/
- public function getTemplateLinksTo( $options = array() ) {
- return $this->getLinksTo( $options, 'templatelinks', 'tl' );
+ public function getTemplateLinksFrom( $options = array() ) {
+ return $this->getLinksFrom( $options, 'templatelinks', 'tl' );
}
/**
* @return Array of Title the Title objects
*/
public function getBrokenLinksFrom() {
- if ( $this->getArticleId() == 0 ) {
+ if ( $this->getArticleID() == 0 ) {
# All links from article ID 0 are false positives
return array();
}
array( 'page', 'pagelinks' ),
array( 'pl_namespace', 'pl_title' ),
array(
- 'pl_from' => $this->getArticleId(),
+ 'pl_from' => $this->getArticleID(),
'page_namespace IS NULL'
),
__METHOD__, array(),
return $status->getErrorsArray();
}
}
+ // Clear RepoGroup process cache
+ RepoGroup::singleton()->clearCache( $this );
+ RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
}
- // Clear RepoGroup process cache
- RepoGroup::singleton()->clearCache( $this );
- RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache
- $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own.
+ $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
$pageid = $this->getArticleID( self::GAID_FOR_UPDATE );
$protected = $this->isProtected();
- $pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 );
// Do the actual move
$err = $this->moveToInternal( $nt, $reason, $createRedirect );
if ( is_array( $err ) ) {
# @todo FIXME: What about the File we have already moved?
- $dbw->rollback();
+ $dbw->rollback( __METHOD__ );
return $err;
}
- $redirid = $this->getArticleID();
-
// Refresh the sortkey for this row. Be careful to avoid resetting
// cl_timestamp, which may disturb time-based lists on some sites.
$prefixes = $dbw->select(
);
}
+ $redirid = $this->getArticleID();
+
if ( $protected ) {
# Protect the redirect title as the title used to be...
$dbw->insertSelect( 'page_restrictions', 'page_restrictions',
WatchedItem::duplicateEntries( $this, $nt );
}
- # Update search engine
- $u = new SearchUpdate( $pageid, $nt->getPrefixedDBkey() );
- $u->doUpdate();
- $u = new SearchUpdate( $redirid, $this->getPrefixedDBkey(), '' );
- $u->doUpdate();
-
- $dbw->commit();
-
- # Update site_stats
- if ( $this->isContentPage() && !$nt->isContentPage() ) {
- # No longer a content page
- # Not viewed, edited, removing
- $u = new SiteStatsUpdate( 0, 1, -1, $pageCountChange );
- } elseif ( !$this->isContentPage() && $nt->isContentPage() ) {
- # Now a content page
- # Not viewed, edited, adding
- $u = new SiteStatsUpdate( 0, 1, + 1, $pageCountChange );
- } elseif ( $pageCountChange ) {
- # Redirect added
- $u = new SiteStatsUpdate( 0, 0, 0, 1 );
- } else {
- # Nothing special
- $u = false;
- }
- if ( $u ) {
- $u->doUpdate();
- }
-
- # Update message cache for interface messages
- if ( $this->getNamespace() == NS_MEDIAWIKI ) {
- # @bug 17860: old article can be deleted, if this the case,
- # delete it from message cache
- if ( $this->getArticleID() === 0 ) {
- MessageCache::singleton()->replace( $this->getDBkey(), false );
- } else {
- $rev = Revision::newFromTitle( $this );
- MessageCache::singleton()->replace( $this->getDBkey(), $rev->getText() );
- }
- }
- if ( $nt->getNamespace() == NS_MEDIAWIKI ) {
- $rev = Revision::newFromTitle( $nt );
- MessageCache::singleton()->replace( $nt->getDBkey(), $rev->getText() );
- }
+ $dbw->commit( __METHOD__ );
wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ) );
return true;
$comment = $wgContLang->truncate( $comment, 255 );
$oldid = $this->getArticleID();
- $latest = $this->getLatestRevID();
$dbw = wfGetDB( DB_MASTER );
- if ( $moveOverRedirect ) {
- $rcts = $dbw->timestamp( $nt->getEarliestRevTime() );
+ $newpage = WikiPage::factory( $nt );
+ if ( $moveOverRedirect ) {
$newid = $nt->getArticleID();
- $newns = $nt->getNamespace();
- $newdbk = $nt->getDBkey();
# 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__ );
- if ( !$dbw->cascadingDeletes() ) {
- $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ );
-
- $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ );
- $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ );
- $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ );
- $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ );
- $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ );
- $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ );
- $dbw->delete( 'iwlinks', array( 'iwl_from' => $newid ), __METHOD__ );
- $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ );
- $dbw->delete( 'page_props', array( 'pp_page' => $newid ), __METHOD__ );
- }
- // If the target page was recently created, it may have an entry in recentchanges still
- $dbw->delete( 'recentchanges',
- array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ),
- __METHOD__
- );
+
+ $newpage->doDeleteUpdates( $newid );
}
# Save a null revision in the page's history notifying of the move
}
$nullRevId = $nullRevision->insertOn( $dbw );
- $now = wfTimestampNow();
# Change the name of the target page:
$dbw->update( 'page',
/* SET */ array(
- 'page_touched' => $dbw->timestamp( $now ),
'page_namespace' => $nt->getNamespace(),
'page_title' => $nt->getDBkey(),
- 'page_latest' => $nullRevId,
),
/* WHERE */ array( 'page_id' => $oldid ),
__METHOD__
);
+
+ $this->resetArticleID( 0 );
$nt->resetArticleID( $oldid );
- $article = WikiPage::factory( $nt );
+ $newpage->updateRevisionOn( $dbw, $nullRevision );
+
wfRunHooks( 'NewRevisionFromEditComplete',
- array( $article, $nullRevision, $latest, $wgUser ) );
- $article->setCachedLastEditTime( $now );
+ array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) );
+
+ $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) );
+
+ if ( !$moveOverRedirect ) {
+ WikiPage::onArticleCreate( $nt );
+ }
# Recreate the redirect, this time in the other direction.
- if ( $createRedirect || !$wgUser->isAllowed( 'suppressredirect' ) ) {
+ if ( $redirectSuppressed ) {
+ WikiPage::onArticleDelete( $this );
+ } else {
$mwRedir = MagicWord::get( 'redirect' );
$redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $nt->getPrefixedText() . "]]\n";
$redirectArticle = WikiPage::factory( $this );
wfRunHooks( 'NewRevisionFromEditComplete',
array( $redirectArticle, $redirectRevision, false, $wgUser ) );
- # Now, we record the link from the redirect to the new title.
- # It should have no other outgoing links...
- $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ );
- $dbw->insert( 'pagelinks',
- array(
- 'pl_from' => $newid,
- 'pl_namespace' => $nt->getNamespace(),
- 'pl_title' => $nt->getDBkey() ),
- __METHOD__ );
+ $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) );
}
- } else {
- $this->resetArticleID( 0 );
}
# Log the move
$logid = $logEntry->insert();
$logEntry->publish( $logid );
-
- # Purge caches for old and new titles
- if ( $moveOverRedirect ) {
- # A simple purge is enough when moving over a redirect
- $nt->purgeSquid();
- } else {
- # Purge caches as per article creation, including any pages that link to this title
- Article::onArticleCreate( $nt );
- }
- $this->purgeSquid();
}
/**
// We don't know whether this function was called before
// or after moving the root page, so check both
// $this and $nt
- if ( $oldSubpage->getArticleId() == $this->getArticleId() ||
- $oldSubpage->getArticleID() == $nt->getArticleId() )
+ if ( $oldSubpage->getArticleID() == $this->getArticleID() ||
+ $oldSubpage->getArticleID() == $nt->getArticleID() )
{
// When moving a page to a subpage of itself,
// don't move it twice
return true;
}
- /**
- * Can this title be added to a user's watchlist?
- *
- * @return Bool TRUE or FALSE
- */
- public function isWatchable() {
- return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() );
- }
-
/**
* Get categories to which this Title belongs and return an array of
* categories' names.
$data = array();
- $titleKey = $this->getArticleId();
+ $titleKey = $this->getArticleID();
if ( $titleKey === 0 ) {
return $data;
$db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
return $db->selectField( 'revision', 'rev_id',
array(
- 'rev_page' => $this->getArticleId( $flags ),
+ 'rev_page' => $this->getArticleID( $flags ),
'rev_id < ' . intval( $revId )
),
__METHOD__,
$db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
return $db->selectField( 'revision', 'rev_id',
array(
- 'rev_page' => $this->getArticleId( $flags ),
+ 'rev_page' => $this->getArticleID( $flags ),
'rev_id > ' . intval( $revId )
),
__METHOD__,
* @return Revision|Null if page doesn't exist
*/
public function getFirstRevision( $flags = 0 ) {
- $pageId = $this->getArticleId( $flags );
+ $pageId = $this->getArticleID( $flags );
if ( $pageId ) {
$db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
$row = $db->selectRow( 'revision', '*',
return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
}
+ /**
+ * 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;
+ }
+
/**
* Get the number of revisions between the given revision.
* Used for diffs and other things that really need it.
$dbr = wfGetDB( DB_SLAVE );
return (int)$dbr->selectField( 'revision', 'count(*)',
array(
- 'rev_page' => $this->getArticleId(),
+ 'rev_page' => $this->getArticleID(),
'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
),
&& strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
}
- /**
- * 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();
- }
- }
-
- /**
- * Return a string representation of this title
- *
- * @return String representation of this title
- */
- public function __toString() {
- return $this->getPrefixedText();
- }
-
/**
* Check if page exists. For historical reasons, this function simply
* checks for the existence of the title in the page table, and will
* @return Bool
*/
public function exists() {
- return $this->getArticleId() != 0;
+ return $this->getArticleID() != 0;
}
/**
* @return Bool
*/
public function isAlwaysKnown() {
+ $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;
+ }
+
if ( $this->mInterwiki != '' ) {
return true; // any interwiki link might be viewable, for all we know
}
+
switch( $this->mNamespace ) {
case NS_MEDIA:
case NS_FILE:
* 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).
+ * Adding something else to this function will cause inconsistency
+ * since LinkHolderArray calls isAlwaysKnown() and does its own
+ * page existence check.
*
* @return Bool
*/
}
/**
- * Is this in a namespace that allows actual pages?
+ * Updates page_touched for this page; called from LinksUpdate.php
*
- * @return Bool
- * @internal note -- uses hardcoded namespace index instead of constants
+ * @return Bool true if the update succeded
*/
- public function canExist() {
- return $this->mNamespace >= 0 && $this->mNamespace != NS_MEDIA;
+ 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;
}
/**
return $prepend . $namespaceKey;
}
- /**
- * Returns true if this is a special page.
- *
- * @return boolean
- */
- public function isSpecialPage() {
- return $this->getNamespace() == NS_SPECIAL;
- }
-
- /**
- * Returns true if this title resolves to the named special page
- *
- * @param $name String The special page name
- * @return boolean
- */
- public function isSpecial( $name ) {
- if ( $this->isSpecialPage() ) {
- list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() );
- if ( $name == $thisName ) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 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
- *
- * @return Title
- */
- 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 );
- }
- }
- }
- return $this;
- }
-
- /**
- * Is this Title in a namespace which contains content?
- * In other words, is this a content page, for the purposes of calculating
- * statistics, etc?
- *
- * @return Boolean
- */
- public function isContentPage() {
- return MWNamespace::isContent( $this->getNamespace() );
- }
-
/**
* Get all extant redirects to this Title
*
}
- /**
- * 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' ) );
- }
-
- wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
-
- wfDebug( __METHOD__ . ': applicable restrictions to [[' .
- $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" );
-
- return $types;
- }
- /**
- * 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 the raw sort key to be used for categories, with the specified
* prefix. This will be fed to Collation::getSortKey() to get a
if ( $this->isSpecialPage() ) {
// special pages are in the user language
return $wgLang;
- } elseif ( $this->isCssOrJsPage() ) {
+ } elseif ( $this->isCssOrJsPage() || $this->isCssJsSubpage() ) {
// css/js should always be LTR and is, in fact, English
return wfGetLangObj( 'en' );
} elseif ( $this->getNamespace() == NS_MEDIAWIKI ) {