* WARNING: This revision requires a schema change, which is included in maintenance/archives/patch-protected_titles.sql, and can be applied in the normal manner (update.php).
* (bug 11657) Support for Thai solar calendar
* (bug 943) RSS feed for Recentchangeslinked
* Introduced AbortMove hook
+* (bug 2919) Protection of nonexistent pages with regular protection interface.
=== Bug fixes in 1.12 ===
$title->touchLinks();
$title->purgeSquid();
+ $title->deleteTitleProtection();
}
static function onArticleDelete( $title ) {
var $mCascade = false;
var $mExpiry = null;
var $mPermErrors = array();
+ var $mApplicableTypes = array();
function __construct( &$article ) {
global $wgRequest, $wgUser;
global $wgRestrictionTypes, $wgRestrictionLevels;
$this->mArticle =& $article;
$this->mTitle =& $article->mTitle;
+ $this->mApplicableTypes = $this->mTitle->exists() ? $wgRestrictionTypes : array('create');
if( $this->mTitle ) {
$this->mTitle->loadRestrictions();
- foreach( $wgRestrictionTypes as $action ) {
+ foreach( $this->mApplicableTypes as $action ) {
// Fixme: this form currently requires individual selections,
// but the db allows multiples separated by commas.
$this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
$this->mCascade = $wgRequest->getBool( 'mwProtect-cascade' );
$this->mExpiry = $wgRequest->getText( 'mwProtect-expiry' );
- foreach( $wgRestrictionTypes as $action ) {
+ foreach( $this->mApplicableTypes as $action ) {
$val = $wgRequest->getVal( "mwProtect-level-$action" );
if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) {
$this->mRestrictions[$action] = $val;
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if( is_null( $this->mTitle ) ||
- !$this->mTitle->exists() ||
$this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
$wgOut->showFatalError( wfMsg( 'badarticleerror' ) );
return;
!(isset($wgGroupPermissions[$edit_restriction]['protect']) && $wgGroupPermissions[$edit_restriction]['protect'] ) )
$this->mCascade = false;
- $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason, $this->mCascade, $expiry );
+ if ($this->mTitle->exists()) {
+ $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason, $this->mCascade, $expiry );
+ } else {
+ $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $this->mReason, $expiry );
+ }
+
if( !$ok ) {
throw new FatalError( "Unknown error at restriction save time." );
}
$out .= "<table id='mwProtectSet'>";
$out .= "<tbody>";
$out .= "<tr>\n";
+
foreach( $this->mRestrictions as $action => $required ) {
/* Not all languages have V_x <-> N_x relation */
$out .= "<th>" . wfMsgHtml( 'restriction-' . $action ) . "</th>\n";
$out .= "<tbody>\n";
global $wgEnableCascadingProtection;
- if( $wgEnableCascadingProtection )
+ if( $wgEnableCascadingProtection && $this->mTitle->exists() )
$out .= '<tr><td></td><td>' . $this->buildCascadeInput() . "</td></tr>\n";
$out .= $this->buildExpiryInput();
'href' => $this->mTitle->getLocalUrl( 'action=history')
);
+ if($wgUser->isAllowed('delete')){
+ $content_actions['delete'] = array(
+ 'class' => ($action == 'delete') ? 'selected' : false,
+ 'text' => wfMsg('delete'),
+ 'href' => $this->mTitle->getLocalUrl( 'action=delete' )
+ );
+ }
+ if ( $this->mTitle->quickUserCan( 'move' ) ) {
+ $moveTitle = SpecialPage::getTitleFor( 'Movepage', $this->thispage );
+ $content_actions['move'] = array(
+ 'class' => $this->mTitle->isSpecial( 'Movepage' ) ? 'selected' : false,
+ 'text' => wfMsg('move'),
+ 'href' => $moveTitle->getLocalUrl()
+ );
+ }
+
if ( $this->mTitle->getNamespace() !== NS_MEDIAWIKI && $wgUser->isAllowed( 'protect' ) ) {
if(!$this->mTitle->isProtected()){
$content_actions['protect'] = array(
);
}
}
- if($wgUser->isAllowed('delete')){
- $content_actions['delete'] = array(
- 'class' => ($action == 'delete') ? 'selected' : false,
- 'text' => wfMsg('delete'),
- 'href' => $this->mTitle->getLocalUrl( 'action=delete' )
- );
- }
- if ( $this->mTitle->quickUserCan( 'move' ) ) {
- $moveTitle = SpecialPage::getTitleFor( 'Movepage', $this->thispage );
- $content_actions['move'] = array(
- 'class' => $this->mTitle->isSpecial( 'Movepage' ) ? 'selected' : false,
- 'text' => wfMsg('move'),
- 'href' => $moveTitle->getLocalUrl()
- );
- }
} else {
//article doesn't exist or is deleted
if( $wgUser->isAllowed( 'delete' ) ) {
);
}
}
+
+ if ( $this->mTitle->getNamespace() !== NS_MEDIAWIKI && $wgUser->isAllowed( 'protect' ) ) {
+ if(!is_array($this->mTitle->getTitleProtection())){
+ $content_actions['protect'] = array(
+ 'class' => ($action == 'protect') ? 'selected' : false,
+ 'text' => wfMsg('protect'),
+ 'href' => $this->mTitle->getLocalUrl( 'action=protect' )
+ );
+
+ } else {
+ $content_actions['unprotect'] = array(
+ 'class' => ($action == 'unprotect') ? 'selected' : false,
+ 'text' => wfMsg('unprotect'),
+ 'href' => $this->mTitle->getLocalUrl( 'action=unprotect' )
+ );
+ }
+ }
}
+
wfProfileOut( "$fname-live" );
if( $this->loggedin ) {
}
}
+
+ if ($action == 'create') {
+ $title_protection = $this->getTitleProtection();
+
+ if (is_array($title_protection)) {
+ extract($title_protection);
+
+ if ($pt_create_perm == 'sysop')
+ $pt_create_perm = 'protect';
+
+ if ($pt_create_perm == '' || !$user->isAllowed($pt_create_perm)) {
+ $errors[] = array ( 'titleprotected', User::whoIs($pt_by), $pt_reason );
+ }
+ }
+ }
+
if( $action == 'create' ) {
if( ( $this->isTalkPage() && !$user->isAllowed( 'createtalk' ) ) ||
( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) ) {
return $errors;
}
+ /**
+ * Is this title subject to title protection?
+ * @return array An associative array representing any existent title protection.
+ */
+ public function getTitleProtection() {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $res = $dbr->select( 'protected_titles', '*',
+ array ('pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBKey()) );
+
+ if ($row = $dbr->fetchRow( $res )) {
+ return $row;
+ } else {
+ return false;
+ }
+ }
+
+ public function updateTitleProtection( $create_perm, $reason, $expiry ) {
+ global $wgGroupPermissions,$wgUser,$wgContLang;
+
+ if ($create_perm == implode(',',$this->getRestrictions('create'))
+ && $expiry == $this->mRestrictionsExpiry) {
+ // No change
+ return true;
+ }
+
+ list ($namespace, $title) = array( $this->getNamespace(), $this->getDBKey() );
+
+ $dbw = wfGetDB( DB_MASTER );
+
+ $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
+
+ $expiry_description = '';
+ if ( $encodedExpiry != 'infinity' ) {
+ $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
+ }
+
+ # 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_by' => $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( $create_perm ) {
+ $log->addEntry( $this->mRestrictions['create'] ? 'modify' : 'protect', $this, trim( $reason . " [create=$create_perm] $expiry_description" ) );
+ } 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__ );
+ }
+
/**
* Can $wgUser edit this page?
* @return boolean
public function loadRestrictions( $oldFashionedRestrictions = NULL ) {
if( !$this->mRestrictionsLoaded ) {
- $dbr = wfGetDB( DB_SLAVE );
+ if ($this->exists()) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $res = $dbr->select( 'page_restrictions', '*',
+ array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
+
+ $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
+ } else {
+ $title_protection = $this->getTitleProtection();
- $res = $dbr->select( 'page_restrictions', '*',
- array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
+ if (is_array($title_protection)) {
+ extract($title_protection);
- $this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
+ $now = wfTimestampNow();
+ $expiry = Block::decodeExpiry($pt_expiry);
+
+ if (!$expiry || $expiry > $now) {
+ // Apply the restrictions
+ $this->mRestrictionsExpiry = $expiry;
+ $this->mRestrictions['create'] = explode(',', trim($pt_create_perm) );
+ } else { // Get rid of the old restrictions
+ Title::purgeExpiredRestrictions();
+ }
+ }
+ $this->mRestrictionsLoaded = true;
+ }
}
}
$dbw->delete( 'page_restrictions',
array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
__METHOD__ );
+
+ $dbw->delete( 'protected_titles',
+ array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
+ __METHOD__ );
}
/**
* @return array the array of groups allowed to edit this article
*/
public function getRestrictions( $action ) {
- if( $this->exists() ) {
- if( !$this->mRestrictionsLoaded ) {
- $this->loadRestrictions();
- }
- return isset( $this->mRestrictions[$action] )
- ? $this->mRestrictions[$action]
- : array();
- } else {
- return array();
+ if( !$this->mRestrictionsLoaded ) {
+ $this->loadRestrictions();
}
+ return isset( $this->mRestrictions[$action] )
+ ? $this->mRestrictions[$action]
+ : array();
}
/**
return Namespace::isContent( $this->getNamespace() );
}
-}
-
-
+}
\ No newline at end of file
'namespaceprotected' => "You do not have permission to edit pages in the '''$1''' namespace.",
'customcssjsprotected' => "You do not have permission to edit this page, because it contains another user's personal settings.",
'ns-specialprotected' => 'Pages in the {{ns:special}} namespace cannot be edited.',
+'titleprotected' => 'This title has been protected from creation by [[User:$1|$1]]. The reason given is <i>$2</i>.',
# Login and logout pages
'logouttitle' => 'User logout',
# Restrictions (nouns)
'restriction-edit' => 'Edit',
'restriction-move' => 'Move',
+'restriction-create' => 'Create',
# Restriction levels
'restriction-level-sysop' => 'full protected',
--- /dev/null
+-- Protected titles - nonexistent pages that have been protected
+CREATE TABLE `/*$wgDBPrefix*/protected_titles` (
+ `pt_namespace` int(11) NOT NULL,
+ `pt_title` varchar(255) NOT NULL,
+ `pt_by` int(10) unsigned NOT NULL,
+ `pt_reason` tinyblob,
+ `pt_timestamp` binary(14) NOT NULL,
+ `pt_expiry` varbinary(14) NOT NULL default '',
+ `pt_create_perm` varbinary(60) NOT NULL,
+ PRIMARY KEY (`pt_namespace`,`pt_title`),
+ KEY `pt_by` (`pt_by`),
+ KEY `pt_timestamp` (`pt_timestamp`)
+) /*$wgDBTableOptions*/
\ No newline at end of file
KEY pr_cascade (pr_cascade)
) /*$wgDBTableOptions*/;
+-- Protected titles - nonexistent pages that have been protected
+CREATE TABLE `/*$wgDBPrefix*/protected_titles` (
+ `pt_namespace` int(11) NOT NULL,
+ `pt_title` varchar(255) NOT NULL,
+ `pt_by` int(10) unsigned NOT NULL,
+ `pt_reason` tinyblob,
+ `pt_timestamp` binary(14) NOT NULL,
+ `pt_expiry` varbinary(14) NOT NULL default '',
+ `pt_create_perm` varbinary(60) NOT NULL,
+ PRIMARY KEY (`pt_namespace`,`pt_title`),
+ KEY `pt_by` (`pt_by`),
+ KEY `pt_timestamp` (`pt_timestamp`)
+) /*$wgDBTableOptions*/
+
-- vim: sw=2 sts=2 et
array( 'filearchive', 'patch-filearchive.sql' ),
array( 'querycachetwo', 'patch-querycachetwo.sql' ),
array( 'redirect', 'patch-redirect.sql' ),
+ array( 'protected_titles', 'patch-protected_titles.sql' ),
);
$wgNewFields = array(