* (bug 2919) Allow the protection of non-existent pages using the regular protection interface.

* 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).
This commit is contained in:
Andrew Garrett 2007-12-11 09:51:56 +00:00
parent 77b2fdec9b
commit 34d22f6edc
9 changed files with 199 additions and 36 deletions

View file

@ -94,6 +94,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
* (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 ===

View file

@ -2898,6 +2898,7 @@ class Article {
$title->touchLinks();
$title->purgeSquid();
$title->deleteTitleProtection();
}
static function onArticleDelete( $title ) {

View file

@ -29,17 +29,19 @@ class ProtectionForm {
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 ) );
@ -67,7 +69,7 @@ class ProtectionForm {
$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;
@ -95,7 +97,6 @@ class ProtectionForm {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if( is_null( $this->mTitle ) ||
!$this->mTitle->exists() ||
$this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
$wgOut->showFatalError( wfMsg( 'badarticleerror' ) );
return;
@ -185,7 +186,12 @@ class ProtectionForm {
!(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." );
}
@ -222,6 +228,7 @@ class ProtectionForm {
$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";
@ -244,7 +251,7 @@ class ProtectionForm {
$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();

View file

@ -718,6 +718,22 @@ class SkinTemplate extends Skin {
'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(
@ -734,21 +750,6 @@ class SkinTemplate extends Skin {
);
}
}
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' ) ) {
@ -762,7 +763,25 @@ class SkinTemplate extends Skin {
);
}
}
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 ) {

View file

@ -1195,6 +1195,22 @@ class Title {
}
}
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' ) ) ) {
@ -1235,6 +1251,77 @@ class Title {
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
@ -1612,12 +1699,32 @@ class Title {
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__ );
$res = $dbr->select( 'page_restrictions', '*',
array ( 'pr_page' => $this->getArticleId() ), __METHOD__ );
$this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
$this->loadRestrictionsFromRow( $res, $oldFashionedRestrictions );
} else {
$title_protection = $this->getTitleProtection();
if (is_array($title_protection)) {
extract($title_protection);
$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;
}
}
}
@ -1629,6 +1736,10 @@ class Title {
$dbw->delete( 'page_restrictions',
array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
__METHOD__ );
$dbw->delete( 'protected_titles',
array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
__METHOD__ );
}
/**
@ -1638,16 +1749,12 @@ class Title {
* @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();
}
/**
@ -2794,6 +2901,4 @@ class Title {
return Namespace::isContent( $this->getNamespace() );
}
}
}

View file

@ -803,6 +803,7 @@ $2',
'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',
@ -1956,6 +1957,7 @@ Here are the current settings for the page <strong>$1</strong>:',
# Restrictions (nouns)
'restriction-edit' => 'Edit',
'restriction-move' => 'Move',
'restriction-create' => 'Create',
# Restriction levels
'restriction-level-sysop' => 'full protected',

View file

@ -0,0 +1,13 @@
-- 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*/

View file

@ -1168,4 +1168,18 @@ CREATE TABLE /*$wgDBprefix*/page_restrictions (
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

View file

@ -38,6 +38,7 @@ $wgNewTables = array(
array( 'filearchive', 'patch-filearchive.sql' ),
array( 'querycachetwo', 'patch-querycachetwo.sql' ),
array( 'redirect', 'patch-redirect.sql' ),
array( 'protected_titles', 'patch-protected_titles.sql' ),
);
$wgNewFields = array(