}
/**
+ * THIS IS NOT THE FUNCTION YOU WANT. Use Title::newFromText().
+ *
+ * Example of wrong and broken code:
+ * $title = Title::newFromURL( $wgRequest->getVal( 'title' ) );
+ *
+ * Example of right code:
+ * $title = Title::newFromText( $wgRequest->getVal( 'title' ) );
+ *
* Create a new Title from URL-encoded text. Ensures that
* the given title's length does not exceed the maximum.
* @param $url \type{\string} the title, as might be taken from a URL
/**
* Create a new Title from an article ID
*
- * @todo This is inefficiently implemented, the page row is requested
- * but not used for anything else
- *
* @param $id \type{\int} the page_id corresponding to the Title to create
* @param $flags \type{\int} use GAID_FOR_UPDATE to use master
* @return \type{Title} the new object, or NULL on an error
*/
public static function newFromID( $id, $flags = 0 ) {
- $fname = 'Title::newFromID';
$db = ($flags & GAID_FOR_UPDATE) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
- $row = $db->selectRow( 'page', array( 'page_namespace', 'page_title' ),
- array( 'page_id' => $id ), $fname );
- if ( $row !== false ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $row = $db->selectRow( 'page', '*', array( 'page_id' => $id ), __METHOD__ );
+ if( $row !== false ) {
+ $title = Title::newFromRow( $row );
} else {
$title = NULL;
}
return trim( $t );
}
- /*
+ /**
* Make a prefixed DB key from a DB key and a namespace index
* @param $ns \type{\int} numerical representation of the namespace
* @param $title \type{\string} the DB key form the title
return $name;
}
- /**
- * Returns the URL associated with an interwiki prefix
- * @param $key \type{\string} the interwiki prefix (e.g. "MeatBall")
- * @return \type{\string} the associated URL, containing "$1",
- * which should be replaced by an article title
- * @static (arguably)
- * @deprecated See Interwiki class
- */
- public function getInterwikiLink( $key ) {
- return Interwiki::fetch( $key )->getURL( );
- }
-
/**
* Determine whether the object refers to a page within
* this project.
* there's a fragment but the prefixed text is empty, we just return a link
* to the fragment.
*
+ * The result obviously should not be URL-escaped, but does need to be
+ * HTML-escaped if it's being output in HTML.
+ *
* @param $query \type{\arrayof{\string}} An associative array of key => value pairs for the
* query string. Keys and values will be escaped.
* @param $variant \type{\string} Language variant of URL (for sr, zh..). Ignored
*/
public function getLinkUrl( $query = array(), $variant = false ) {
wfProfileIn( __METHOD__ );
- if( !is_array( $query ) ) {
- wfProfileOut( __METHOD__ );
- throw new MWException( 'Title::getLinkUrl passed a non-array for '.
- '$query' );
- }
if( $this->isExternal() ) {
$ret = $this->getFullURL( $query );
} elseif( $this->getPrefixedText() === '' && $this->getFragment() !== '' ) {
if( !$user->isAllowed( 'move' ) ) {
// User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ 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' );
+ } else {
+ $errors[] = array ('movenotallowed');
+ }
}
} elseif ( $action == 'create' ) {
if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
} elseif( $action == 'move-target' ) {
if( !$user->isAllowed( 'move' ) ) {
// User can't move anything
- $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+ $errors[] = array ('movenotallowed');
} elseif( !$user->isAllowed( 'move-rootuserpages' )
&& $this->getNamespace() == NS_USER && !$this->isSubpage() )
{
# Protect css/js subpages of user pages
# XXX: this might be better using restrictions
- # XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
- if( $this->isCssJsSubpage() && !$user->isAllowed('editusercssjs')
+ # 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') )
+ && $action != 'patrol'
+ && !preg_match('/^'.preg_quote($user->getName(), '/').'\//', $this->mTextform) )
+ {
+ $errors[] = array('customcssjsprotected');
+ } else if( $this->isJsSubpage() && ( !$user->isAllowed('editusercssjs') || !$user->isAllowed('edituserjs') )
+ && $action != 'patrol'
&& !preg_match('/^'.preg_quote($user->getName(), '/').'\//', $this->mTextform) )
{
$errors[] = array('customcssjsprotected');
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', '*',
$expiry_description = '';
if ( $encodedExpiry != 'infinity' ) {
- $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) , $wgContLang->date( $expiry ) , $wgContLang->time( $expiry ) ).')';
+ $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 != '' ) {
$dbw->replace( 'protected_titles', array(array('pt_namespace', 'pt_title')),
- array( 'pt_namespace' => $namespace, 'pt_title' => $title
- , 'pt_create_perm' => $create_perm
- , 'pt_timestamp' => Block::encodeExpiry(wfTimestampNow(), $dbw)
- , 'pt_expiry' => $encodedExpiry
- , 'pt_user' => $wgUser->getId(), 'pt_reason' => $reason ), __METHOD__ );
+ array(
+ 'pt_namespace' => $namespace,
+ 'pt_title' => $title,
+ 'pt_create_perm' => $create_perm,
+ 'pt_timestamp' => Block::encodeExpiry(wfTimestampNow(), $dbw),
+ 'pt_expiry' => $encodedExpiry,
+ 'pt_user' => $wgUser->getId(),
+ 'pt_reason' => $reason,
+ ), __METHOD__
+ );
} else {
$dbw->delete( 'protected_titles', array( 'pt_namespace' => $namespace,
'pt_title' => $title ), __METHOD__ );
}
# Update the protection log
- $log = new LogPage( 'protect' );
+ if( $dbw->affectedRows() ) {
+ $log = new LogPage( 'protect' );
- if( $create_perm ) {
- $params = array("[create=$create_perm] $expiry_description",'');
- $log->addEntry( $this->mRestrictions['create'] ? 'modify' : 'protect', $this, trim( $reason ), $params );
- } else {
- $log->addEntry( 'unprotect', $this, $reason );
+ 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;
__METHOD__ );
}
- /**
- * Can $wgUser edit this page?
- * @return \type{\bool} TRUE or FALSE
- * @deprecated use userCan('edit')
- */
- public function userCanEdit( $doExpensiveQueries = true ) {
- return $this->userCan( 'edit', $doExpensiveQueries );
- }
-
- /**
- * Can $wgUser create this page?
- * @return \type{\bool} TRUE or FALSE
- * @deprecated use userCan('create')
- */
- public function userCanCreate( $doExpensiveQueries = true ) {
- return $this->userCan( 'create', $doExpensiveQueries );
- }
-
- /**
- * Can $wgUser move this page?
- * @return \type{\bool} TRUE or FALSE
- * @deprecated use userCan('move')
- */
- public function userCanMove( $doExpensiveQueries = true ) {
- return $this->userCan( 'move', $doExpensiveQueries );
- }
-
/**
* Would anybody with sufficient privileges be able to move this page?
* Some pages just aren't movable.
*/
public function userCanRead() {
global $wgUser, $wgGroupPermissions;
-
+
+ static $useShortcut = null;
+
+ # Initialize the $useShortcut boolean, to determine if we can skip quite a bit of code below
+ if( is_null( $useShortcut ) ) {
+ global $wgRevokePermissions;
+ $useShortcut = true;
+ if( empty( $wgGroupPermissions['*']['read'] ) ) {
+ # Not a public wiki, so no shortcut
+ $useShortcut = false;
+ } elseif( !empty( $wgRevokePermissions ) ) {
+ /*
+ * Iterate through each group with permissions being revoked (key not included since we don't care
+ * what the group name is), then check if the read permission is being revoked. If it is, then
+ * we don't use the shortcut below since the user might not be able to read, even though anon
+ * reading is allowed.
+ */
+ foreach( $wgRevokePermissions as $perms ) {
+ if( !empty( $perms['read'] ) ) {
+ # We might be removing the read right from the user, so no shortcut
+ $useShortcut = false;
+ break;
+ }
+ }
+ }
+ }
+
$result = null;
wfRunHooks( 'userCan', array( &$this, &$wgUser, 'read', &$result ) );
if ( $result !== null ) {
}
# Shortcut for public wikis, allows skipping quite a bit of code
- if ( !empty( $wgGroupPermissions['*']['read'] ) )
+ if ( $useShortcut )
return true;
if( $wgUser->isAllowed( 'read' ) ) {
return ( NS_USER == $this->mNamespace && preg_match("/\\/.*\\.js$/", $this->mTextform ) );
}
/**
- * Protect css/js subpages of user pages: can $wgUser edit
+ * Protect css subpages of user pages: can $wgUser edit
* this page?
*
* @return \type{\bool} TRUE or FALSE
* @todo XXX: this might be better using restrictions
*/
- public function userCanEditCssJsSubpage() {
+ public function userCanEditCssSubpage() {
global $wgUser;
- return ( $wgUser->isAllowed('editusercssjs') || preg_match('/^'.preg_quote($wgUser->getName(), '/').'\//', $this->mTextform) );
+ return ( ( $wgUser->isAllowed('editusercssjs') && $wgUser->isAllowed('editusercss') )
+ || preg_match('/^'.preg_quote($wgUser->getName(), '/').'\//', $this->mTextform) );
+ }
+ /**
+ * Protect js subpages of user pages: can $wgUser edit
+ * this page?
+ *
+ * @return \type{\bool} TRUE or FALSE
+ * @todo XXX: this might be better using restrictions
+ */
+ public function userCanEditJsSubpage() {
+ global $wgUser;
+ return ( ( $wgUser->isAllowed('editusercssjs') && $wgUser->isAllowed('edituserjs') )
+ || preg_match('/^'.preg_quote($wgUser->getName(), '/').'\//', $this->mTextform) );
}
/**
* Loads a string into mRestrictions array
* @param $res \type{Resource} restrictions as an SQL result.
*/
- private function loadRestrictionsFromRow( $res, $oldFashionedRestrictions = NULL ) {
+ 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 ) {
global $wgRestrictionTypes;
$dbr = wfGetDB( DB_SLAVE );
}
- 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
$res = $dbr->select( 'page_restrictions', '*',
array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
- $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
+ $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
} else {
$title_protection = $this->getTitleProtection();
#
$dbkey = preg_replace( '/[ _]+/', '_', $dbkey );
$dbkey = trim( $dbkey, '_' );
-
- # Clean up Arabic harakats (bug 16899)
- $dbkey = preg_replace( '/[\x{064B}-\x{0652}]/Su', '', $dbkey );
if ( '' == $dbkey ) {
return false;
if( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) {
$errors[] = array('imageinvalidfilename');
}
- if( !File::checkExtensionCompatibility( $file, $nt->getDBKey() ) ) {
+ if( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) {
$errors[] = array('imagetypemismatch');
}
}
$nt->getUserPermissionsErrors('edit', $wgUser) );
}
- $match = EditPage::matchSpamRegex( $reason );
+ $match = EditPage::matchSummarySpamRegex( $reason );
if( $match !== false ) {
// This is kind of lame, won't display nice
$errors[] = array('spamprotectiontext');
return $err;
}
+ // If it is a file, more it first. It is done before all other moving stuff is done because it's hard to revert
+ $dbw = wfGetDB( DB_MASTER );
+ if( $this->getNamespace() == NS_FILE ) {
+ $file = wfLocalFile( $this );
+ if( $file->exists() ) {
+ $status = $file->move( $nt );
+ if( !$status->isOk() ) {
+ return $status->getErrorsArray();
+ }
+ }
+ }
+
$pageid = $this->getArticleID();
$protected = $this->isProtected();
if( $nt->exists() ) {
// we can't actually distinguish it from a default here, and it'll
// be set to the new title even though it really shouldn't.
// It'll get corrected on the next edit, but resetting cl_timestamp.
- $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'categorylinks',
array(
'cl_sortkey' => $nt->getPrefixedText(),
# Update message cache for interface messages
if( $nt->getNamespace() == NS_MEDIAWIKI ) {
global $wgMessageCache;
- $oldarticle = new Article( $this );
- $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() );
+
+ # @bug 17860: old article can be deleted, if this the case,
+ # delete it from message cache
+ if ( $this->getArticleID() === 0 ) {
+ $wgMessageCache->replace( $this->getDBkey(), false );
+ } else {
+ $oldarticle = new Article( $this );
+ $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() );
+ }
+
$newarticle = new Article( $nt );
$wgMessageCache->replace( $nt->getDBkey(), $newarticle->getContent() );
}
$dbw = wfGetDB( DB_MASTER );
+ $rcts = $dbw->timestamp( $nt->getEarliestRevTime() );
+ $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
$dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ );
$dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ );
}
+ // If the redirect 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__
+ );
# Save a null revision in the page's history notifying of the move
$nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true );
$redirectSuppressed = true;
}
- # Move an image if this is a file
- if( $this->getNamespace() == NS_FILE ) {
- $file = wfLocalFile( $this );
- if( $file->exists() ) {
- $status = $file->move( $nt );
- if( !$status->isOk() ) {
- $dbw->rollback();
- return $status->getErrorsArray();
- }
- }
- }
-
# Log the move
$log = new LogPage( 'move' );
$log->addEntry( 'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) );
$redirectSuppressed = true;
}
- # Move an image if this is a file
- if( $this->getNamespace() == NS_FILE ) {
- $file = wfLocalFile( $this );
- if( $file->exists() ) {
- $status = $file->move( $nt );
- if( !$status->isOk() ) {
- $dbw->rollback();
- return $status->getErrorsArray();
- }
- }
- }
-
# Log the move
$log = new LogPage( 'move' );
$log->addEntry( 'move', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) );
* arrays (errors) as values, or an error array with numeric indices if no pages were moved
*/
public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) {
- global $wgUser, $wgMaximumMovedPages;
+ global $wgMaximumMovedPages;
// Check permissions
if( !$this->userCan( 'move-subpages' ) )
return array( 'cant-move-subpages' );
// don't move it twice
continue;
$newPageName = preg_replace(
- '#^'.preg_quote( $this->getDBKey(), '#' ).'#',
- $nt->getDBKey(), $oldSubpage->getDBKey() );
+ '#^'.preg_quote( $this->getDBkey(), '#' ).'#',
+ $nt->getDBkey(), $oldSubpage->getDBkey() );
if( $oldSubpage->isTalkPage() ) {
$newNs = $nt->getTalkPage()->getNamespace();
} else {
case NS_FILE:
return wfFindFile( $this ); // file exists, possibly in a foreign repo
case NS_SPECIAL:
- return SpecialPage::exists( $this->getDBKey() ); // valid special page
+ return SpecialPage::exists( $this->getDBkey() ); // valid special page
case NS_MAIN:
return $this->mDbkeyform == ''; // selflink, possibly with fragment
case NS_MEDIAWIKI:
* @return \type{\string} Trackback URL
*/
public function trackbackURL() {
- global $wgScriptPath, $wgServer;
+ global $wgScriptPath, $wgServer, $wgScriptExtension;
- return "$wgServer$wgScriptPath/trackback.php?article="
+ return "$wgServer$wgScriptPath/trackback$wgScriptExtension?article="
. htmlspecialchars(urlencode($this->getPrefixedDBkey()));
}
* Generate strings used for xml 'id' names in monobook tabs
* @return \type{\string} XML 'id' name
*/
- public function getNamespaceKey() {
- global $wgContLang;
- switch ($this->getNamespace()) {
- case NS_MAIN:
- case NS_TALK:
- return 'nstab-main';
- case NS_USER:
- case NS_USER_TALK:
- return 'nstab-user';
- case NS_MEDIA:
- return 'nstab-media';
- case NS_SPECIAL:
- return 'nstab-special';
- case NS_PROJECT:
- case NS_PROJECT_TALK:
- return 'nstab-project';
- case NS_FILE:
- case NS_FILE_TALK:
- return 'nstab-image';
- case NS_MEDIAWIKI:
- case NS_MEDIAWIKI_TALK:
- return 'nstab-mediawiki';
- case NS_TEMPLATE:
- case NS_TEMPLATE_TALK:
- return 'nstab-template';
- case NS_HELP:
- case NS_HELP_TALK:
- return 'nstab-help';
- case NS_CATEGORY:
- case NS_CATEGORY_TALK:
- return 'nstab-category';
- default:
- return 'nstab-' . $wgContLang->lc( $this->getSubjectNsText() );
+ public function getNamespaceKey( $prepend = 'nstab-' ) {
+ global $wgContLang, $wgCanonicalNamespaceNames;
+ // Gets the subject namespace if this title
+ $namespace = MWNamespace::getSubject( $this->getNamespace() );
+ // Checks if cononical namespace name exists for namespace
+ if ( isset( $wgCanonicalNamespaceNames[$namespace] ) ) {
+ // Uses canonical namespace name
+ $namespaceKey = $wgCanonicalNamespaceNames[$namespace];
+ } else {
+ // Uses text of namespace
+ $namespaceKey = $this->getSubjectNsText();
+ }
+ // Makes namespace key lowercase
+ $namespaceKey = $wgContLang->lc( $namespaceKey );
+ // Uses main
+ if ( $namespaceKey == '' ) {
+ $namespaceKey = 'main';
+ }
+ // Changes file to image for backwards compatibility
+ if ( $namespaceKey == 'file' ) {
+ $namespaceKey = 'image';
}
+ return $prepend . $namespaceKey;
}
/**