* @return \type{\string} Namespace text
*/
public function getNsText() {
- global $wgContLang, $wgCanonicalNamespaceNames;
+ global $wgContLang;
if ( '' != $this->mInterwiki ) {
// This probably shouldn't even happen. ohh man, oh yuck.
//
// Use the canonical namespaces if possible to try to
// resolve a foreign namespace.
- if( isset( $wgCanonicalNamespaceNames[$this->mNamespace] ) ) {
- return $wgCanonicalNamespaceNames[$this->mNamespace];
+ if( MWNamespace::exists( $this->mNamespace ) ) {
+ return MWNamespace::getCanonicalName( $this->mNamespace );
}
}
return $wgContLang->getNsText( $this->mNamespace );
$interwiki = Interwiki::fetch( $this->mInterwiki );
if ( !$interwiki ) {
- $url = $this->getLocalUrl( $query, $variant );
+ $url = $this->getLocalURL( $query, $variant );
// Ugly quick hack to avoid duplicate prefixes (bug 4571 etc)
// Correct fix would be to move the prepending elsewhere.
/**
* Does the title correspond to a protected article?
* @param $what \type{\string} the action the page is protected from,
- * by default checks move and edit
+ * by default checks all actions.
* @return \type{\bool}
*/
public function isProtected( $action = '' ) {
- global $wgRestrictionLevels, $wgRestrictionTypes;
+ global $wgRestrictionLevels;
+
+ $restrictionTypes = $this->getRestrictionTypes();
# Special pages have inherent protection
if( $this->getNamespace() == NS_SPECIAL )
return true;
# Check regular protection levels
- foreach( $wgRestrictionTypes as $type ){
+ foreach( $restrictionTypes as $type ){
if( $action == $type || $action == '' ) {
$r = $this->getRestrictions( $type );
foreach( $wgRestrictionLevels as $level ) {
/**
* Can $wgUser perform $action on this page?
- * This skips potentially expensive cascading permission checks.
+ * This skips potentially expensive cascading permission checks
+ * as well as avoids expensive error formatting
*
* Suitable for use for nonessential UI controls in common cases, but
* _not_ for functional access control.
}
} elseif( !$user->isAllowed( $action ) ) {
$return = null;
- $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
- User::getGroupsWithPermission( $action ) );
+
+ // We avoid expensive display logic for quickUserCan's and such
+ $groups = false;
+ if (!$short) {
+ $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
+ User::getGroupsWithPermission( $action ) );
+ }
+
if( $groups ) {
$return = array( 'badaccess-groups',
array( implode( ', ', $groups ), count( $groups ) ) );
# XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssSubpage()
# and $this->userCanEditJsSubpage() from working
# XXX: right 'editusercssjs' is deprecated, for backward compatibility only
- if( $this->isCssSubpage() && ( !$user->isAllowed('editusercssjs') || !$user->isAllowed('editusercss') )
+ if( $this->isCssSubpage() && !( $user->isAllowed('editusercssjs') || $user->isAllowed('editusercss') )
&& $action != 'patrol'
&& !preg_match('/^'.preg_quote($user->getName(), '/').'\//', $this->mTextform) )
{
$errors[] = array('customcssjsprotected');
- } else if( $this->isJsSubpage() && ( !$user->isAllowed('editusercssjs') || !$user->isAllowed('edituserjs') )
+ } else if( $this->isJsSubpage() && !( $user->isAllowed('editusercssjs') || $user->isAllowed('edituserjs') )
&& $action != 'patrol'
&& !preg_match('/^'.preg_quote($user->getName(), '/').'\//', $this->mTextform) )
{
if ( $this->getNamespace() < 0 ) {
return false;
}
+
+ // Can't protect pages that exist.
+ if ($this->exists()) {
+ return false;
+ }
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'protected_titles', '*',
$dbr = wfGetDB( DB_SLAVE );
$conds['page_namespace'] = $this->getNamespace();
- $conds[] = 'page_title LIKE ' . $dbr->addQuotes(
- $dbr->escapeLike( $this->getDBkey() ) . '/%' );
+ $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
$options = array();
if( $limit > -1 )
$options['LIMIT'] = $limit;
* The restriction array is an array of each type, each of which contains an array of unique groups.
*/
public function getCascadeProtectionSources( $get_pages = true ) {
- global $wgRestrictionTypes;
-
- # Define our dimension of restrictions types
$pagerestrictions = array();
- foreach( $wgRestrictionTypes as $action )
- $pagerestrictions[$action] = array();
if ( isset( $this->mCascadeSources ) && $get_pages ) {
return array( $this->mCascadeSources, $this->mCascadingRestrictions );
$sources[$page_id] = Title::makeTitle($page_ns, $page_title);
# Add groups needed for each restriction type if its not already there
# Make sure this restriction type still exists
- if ( isset($pagerestrictions[$row->pr_type]) && !in_array($row->pr_level, $pagerestrictions[$row->pr_type]) ) {
+
+ if ( !isset( $pagerestrictions[$row->pr_type] ) ) {
+ $pagerestrictions[$row->pr_type] = array();
+ }
+
+ if ( isset($pagerestrictions[$row->pr_type]) &&
+ !in_array($row->pr_level, $pagerestrictions[$row->pr_type]) ) {
$pagerestrictions[$row->pr_type][]=$row->pr_level;
}
} else {
* Loads a string into mRestrictions array
* @param $res \type{Resource} restrictions as an SQL result.
*/
- private function loadRestrictionsFromRow( $res, $oldFashionedRestrictions = NULL ) {
- global $wgRestrictionTypes;
+ private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = NULL ) {
+ $rows = array();
$dbr = wfGetDB( DB_SLAVE );
+
+ while( $row = $dbr->fetchObject( $res ) ) {
+ $rows[] = $row;
+ }
+
+ $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
+ }
+
+ public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = NULL ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $restrictionTypes = $this->getRestrictionTypes();
- foreach( $wgRestrictionTypes as $type ){
+ foreach( $restrictionTypes as $type ){
$this->mRestrictions[$type] = array();
$this->mRestrictionsExpiry[$type] = Block::decodeExpiry('');
}
}
- if( $dbr->numRows( $res ) ) {
+ if( count($rows) ) {
# Current system - load second to make them override.
$now = wfTimestampNow();
$purgeExpired = false;
- foreach( $res as $row ) {
+ foreach( $rows as $row ) {
# Cycle through all the restrictions.
- // Don't take care of restrictions types that aren't in $wgRestrictionTypes
- if( !in_array( $row->pr_type, $wgRestrictionTypes ) )
+ // Don't take care of restrictions types that aren't allowed
+
+ if( !in_array( $row->pr_type, $restrictionTypes ) )
continue;
// This code should be refactored, now that it's being used more generally,
$res = $dbr->select( 'page_restrictions', '*',
array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
- $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
+ $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
} else {
$title_protection = $this->getTitleProtection();
/**
* What is the page_latest field for this page?
* @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update
- * @return \type{\int}
+ * @return \type{\int} or false if the page doesn't exist
*/
public function getLatestRevID( $flags = 0 ) {
if( $this->mLatestID !== false )
$linkCache->clearBadLink( $this->getPrefixedDBkey() );
if ( $newid === false ) { $this->mArticleID = -1; }
- else { $this->mArticleID = $newid; }
+ else { $this->mArticleID = intval( $newid ); }
$this->mRestrictionsLoaded = false;
$this->mRestrictions = array();
}
}
return $p . $name;
}
-
- /**
- * 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.
- * @return \type{\bool} true on success
- */
- private function secureAndSplit() {
- global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks;
-
- # Initialisation
+
+ // 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.
+ static function getTitleInvalidRegex() {
static $rxTc = false;
if( !$rxTc ) {
# Matching titles will be held as illegal.
'|&#x[0-9A-Fa-f]+;' .
'/S';
}
+
+ return $rxTc;
+ }
+
+ /**
+ * Capitalize a text if it belongs to a namespace that capitalizes
+ */
+ public static function capitalize( $text, $ns = NS_MAIN ) {
+ global $wgContLang;
+
+ if ( MWNamespace::isCapitalized( $ns ) )
+ return $wgContLang->ucfirst( $text );
+ else
+ return $text;
+ }
+
+ /**
+ * 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.
+ * @return \type{\bool} true on success
+ */
+ private function secureAndSplit() {
+ global $wgContLang, $wgLocalInterwiki;
+
+ # Initialisation
+ $rxTc = self::getTitleInvalidRegex();
$this->mInterwiki = $this->mFragment = '';
$this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
$dbkey = preg_replace( '/\xE2\x80[\x8E\x8F\xAA-\xAE]/S', '', $dbkey );
# Clean up whitespace
+ # 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.
#
- $dbkey = preg_replace( '/[ _]+/', '_', $dbkey );
+ $dbkey = preg_replace( '/[ _\xA0\x{1680}\x{180E}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}]+/u', '_', $dbkey );
$dbkey = trim( $dbkey, '_' );
if ( '' == $dbkey ) {
* site might be case-sensitive.
*/
$this->mUserCaseDBKey = $dbkey;
- if( $wgCapitalLinks && $this->mInterwiki == '') {
- $dbkey = $wgContLang->ucfirst( $dbkey );
+ if( $this->mInterwiki == '') {
+ $dbkey = self::capitalize( $dbkey, $this->mNamespace );
}
/**
*/
private function moveOverExistingRedirect( &$nt, $reason = '', $createRedirect = true ) {
global $wgUseSquid, $wgUser;
- $fname = 'Title::moveOverExistingRedirect';
+
$comment = wfMsgForContent( '1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText() );
if ( $reason ) {
- $comment .= ": $reason";
+ $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
}
$now = wfTimestampNow();
$newid = $nt->getArticleID();
$oldid = $this->getArticleID();
$latest = $this->getLatestRevID();
+
+ $dbw = wfGetDB( DB_MASTER );
+
$rcts = $dbw->timestamp( $nt->getEarliestRevTime() );
$newns = $nt->getNamespace();
$newdbk = $nt->getDBkey();
- $dbw = wfGetDB( DB_MASTER );
-
# 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 ), $fname );
+ $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
if ( !$dbw->cascadingDeletes() ) {
$dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ );
global $wgUseTrackbacks;
'page_latest' => $nullRevId,
),
/* WHERE */ array( 'page_id' => $oldid ),
- $fname
+ __METHOD__
);
$nt->resetArticleID( $oldid );
# 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 ), $fname );
+ $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ );
$dbw->insert( 'pagelinks',
array(
'pl_from' => $newid,
'pl_namespace' => $nt->getNamespace(),
'pl_title' => $nt->getDBkey() ),
- $fname );
+ __METHOD__ );
$redirectSuppressed = false;
} else {
$this->resetArticleID( 0 );
*/
private function moveToNewTitle( &$nt, $reason = '', $createRedirect = true ) {
global $wgUseSquid, $wgUser;
- $fname = 'MovePageForm::moveToNewTitle';
+
$comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
if ( $reason ) {
$comment .= wfMsgExt( 'colon-separator',
# Save a null revision in the page's history notifying of the move
$nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
+ if ( !is_object( $nullRevision ) ) {
+ throw new MWException( 'No valid null revision produced in ' . __METHOD__ );
+ }
$nullRevId = $nullRevision->insertOn( $dbw );
$article = new Article( $this );
'page_latest' => $nullRevId,
),
/* WHERE */ array( 'page_id' => $oldid ),
- $fname
+ __METHOD__
);
$nt->resetArticleID( $oldid );
'pl_from' => $newid,
'pl_namespace' => $nt->getNamespace(),
'pl_title' => $nt->getDBkey() ),
- $fname );
+ __METHOD__ );
$redirectSuppressed = false;
} else {
$this->resetArticleID( 0 );
break;
}
- if( $oldSubpage->getArticleId() == $this->getArticleId() )
+ // 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() )
// When moving a page to a subpage of itself,
// don't move it twice
continue;
$newPageName = preg_replace(
'#^'.preg_quote( $this->getDBkey(), '#' ).'#',
- $nt->getDBkey(), $oldSubpage->getDBkey() );
+ str_replace( '\\', '\\\\', $nt->getDBkey() ), # bug 21234
+ $oldSubpage->getDBkey() );
if( $oldSubpage->isTalkPage() ) {
$newNs = $nt->getTalkPage()->getNamespace();
} else {
*/
public function countRevisionsBetween( $old, $new ) {
$dbr = wfGetDB( DB_SLAVE );
- return $dbr->selectField( 'revision', 'count(*)',
+ return (int)$dbr->selectField( 'revision', 'count(*)',
'rev_page = ' . intval( $this->getArticleId() ) .
' AND rev_id > ' . intval( $old ) .
' AND rev_id < ' . intval( $new ),
* @return \type{\string} XML 'id' name
*/
public function getNamespaceKey( $prepend = 'nstab-' ) {
- global $wgContLang, $wgCanonicalNamespaceNames;
+ global $wgContLang;
// Gets the subject namespace if this title
$namespace = MWNamespace::getSubject( $this->getNamespace() );
// Checks if cononical namespace name exists for namespace
- if ( isset( $wgCanonicalNamespaceNames[$namespace] ) ) {
+ if ( MWNamespace::exists( $this->getNamespace() ) ) {
// Uses canonical namespace name
- $namespaceKey = $wgCanonicalNamespaceNames[$namespace];
+ $namespaceKey = MWNamespace::getCanonicalName( $namespace );
} else {
// Uses text of namespace
$namespaceKey = $this->getSubjectNsText();
}
return $this->mBacklinkCache;
}
+
+ /**
+ * Whether the magic words __INDEX__ and __NOINDEX__ function for
+ * this page.
+ * @return Bool
+ */
+ public function canUseNoindex(){
+ global $wgArticleRobotPolicies, $wgContentNamespaces,
+ $wgExemptFromUserRobotsControl;
+
+ $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
+ ? $wgContentNamespaces
+ : $wgExemptFromUserRobotsControl;
+
+ return !in_array( $this->mNamespace, $bannedNamespaces );
+
+ }
+
+ public function getRestrictionTypes() {
+ global $wgRestrictionTypes;
+ $types = $this->exists() ? $wgRestrictionTypes : array('create');
+
+ if ( $this->getNamespace() == NS_FILE ) {
+ $types[] = 'upload';
+ }
+
+ wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) );
+
+ return $types;
+ }
}