and their protection status (full protection status is not pulled out due
to performance considerations, so it just shows "full protected" or
"semi protected".
+* (bug 4133) Allow page protections to be made with an expiry date, in the same format
+ as block expiry dates. Existing protections are assumed to be infinite, as are protections
+ made with the new field left blank.
== Languages updated ==
* @param string $reason
* @return bool true on success
*/
- function updateRestrictions( $limit = array(), $reason = '', $cascade = 0 ) {
+ function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) {
global $wgUser, $wgRestrictionTypes, $wgContLang;
$id = $this->mTitle->getArticleID();
$cascade = false;
}
+ // Take this opportunity to purge out expired restrictions
+ Title::purgeExpiredRestrictions();
+
# FIXME: Same limitations as described in ProtectionForm.php (line 37);
# we expect a single selection, but the schema allows otherwise.
$current = array();
$changed = ( $current != $updated );
$changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
# If nothing's changed, do nothing
# Update restrictions table
foreach( $limit as $action => $restrictions ) {
+ $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
+
if ($restrictions != '' ) {
$dbw->replace( 'page_restrictions', array( 'pr_pagetype'),
array( 'pr_page' => $id, 'pr_type' => $action
- , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0 ), __METHOD__ );
+ , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
+ , 'pr_expiry' => $encodedExpiry ), __METHOD__ );
} else {
$dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
'pr_type' => $action ), __METHOD__ );
$log = new LogPage( 'protect' );
$cascade_description = '';
+ $expiry_description = '';
if ($cascade) {
$cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
}
+ if ( $encodedExpiry != 'infinity' ) {
+ $expiry_description = ' ' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) );
+ }
+
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description" ) );
+ $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
var $mRestrictions = array();
var $mReason = '';
var $mCascade = false;
+ var $mExpiry = null;
function __construct( &$article ) {
- global $wgRequest, $wgUser;
+ global $wgRequest, $wgUser, $wgLang;
global $wgRestrictionTypes, $wgRestrictionLevels;
$this->mArticle =& $article;
$this->mTitle =& $article->mTitle;
if( $this->mTitle ) {
+ $this->mTitle->loadRestrictions();
+
foreach( $wgRestrictionTypes as $action ) {
// Fixme: this form currently requires individual selections,
// but the db allows multiples separated by commas.
}
$this->mCascade = $this->mTitle->areRestrictionsCascading();
+
+ if ( $this->mTitle->mRestrictionsExpiry == 'infinity' ) {
+ $this->mExpiry = 'infinite';
+ } else if ( strlen($this->mTitle->mRestrictionsExpiry) == 0 ) {
+ $this->mExpiry = '';
+ } else {
+ $this->mExpiry = $wgLang->timeanddate( $this->mTitle->mRestrictionsExpiry );
+ }
}
// The form will be available in read-only to show levels.
if( $wgRequest->wasPosted() ) {
$this->mReason = $wgRequest->getText( 'mwProtect-reason' );
$this->mCascade = $wgRequest->getBool( 'mwProtect-cascade' );
+ $this->mExpiry = $wgRequest->getText( 'mwProtect-expiry' );
+
foreach( $wgRestrictionTypes as $action ) {
$val = $wgRequest->getVal( "mwProtect-level-$action" );
if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) {
$this->mRestrictions[$action] = $val;
}
}
+
+ if( $this->save() ) {
+ global $wgOut;
+ $wgOut->redirect( $this->mTitle->getFullUrl() );
+ return;
+ }
}
}
- function show() {
+ function show( $err = null ) {
global $wgOut;
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$cascadeSources = $this->mTitle->getCascadeProtectionSources();
+ if ( "" != $err ) {
+ $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
+ $wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
+ }
+
if ( $cascadeSources && count($cascadeSources) > 0 ) {
$titles = '';
$wgOut->addWikiText( $notice );
}
- if( $this->save() ) {
- $wgOut->redirect( $this->mTitle->getFullUrl() );
- return;
- }
-
$wgOut->setPageTitle( wfMsg( 'confirmprotect' ) );
$wgOut->setSubtitle( wfMsg( 'protectsub', $this->mTitle->getPrefixedText() ) );
throw new FatalError( wfMsg( 'sessionfailure' ) );
}
- $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason, $this->mCascade );
+ if ( strlen( $this->mExpiry ) == 0 ) {
+ $this->mExpiry = 'infinite';
+ }
+
+ if ( $this->mExpiry == 'infinite' || $this->mExpiry == 'indefinite' ) {
+ $expiry = Block::infinity();
+ } else {
+ # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1
+ $expiry = strtotime( $this->mExpiry );
+
+ if ( $expiry < 0 || $expiry === false ) {
+ $this->show( wfMsg( 'protect_expiry_invalid' ) );
+ return;
+ }
+
+ $expiry = wfTimestamp( TS_MW, $expiry );
+ }
+
+ $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason, $this->mCascade, $expiry );
if( !$ok ) {
throw new FatalError( "Unknown error at restriction save time." );
}
$out .= "</tbody>\n";
$out .= "</table>\n";
+ $out .= "<table>\n";
+ $out .= "<tbody>\n";
+
global $wgEnableCascadingProtection;
if ($wgEnableCascadingProtection)
$out .= $this->buildCascadeInput();
+ $out .= $this->buildExpiryInput();
+
if( !$this->disabled ) {
- $out .= "<table>\n";
- $out .= "<tbody>\n";
$out .= "<tr><td>" . $this->buildReasonInput() . "</td></tr>\n";
$out .= "<tr><td></td><td>" . $this->buildSubmit() . "</td></tr>\n";
- $out .= "</tbody>\n";
- $out .= "</table>\n";
+ }
+
+ $out .= "</tbody>\n";
+ $out .= "</table>\n";
+
+ if ( !$this->disabled ) {
$out .= "</form>\n";
$out .= $this->buildCleanupScript();
}
function buildCascadeInput() {
$id = 'mwProtect-cascade';
- $ci = wfCheckLabel( wfMsg( 'protect-cascade' ), $id, $id, $this->mCascade, $this->disabledAttrib);
+ $ci = "<tr>" . wfCheckLabel( wfMsg( 'protect-cascade' ), $id, $id, $this->mCascade, $this->disabledAttrib) . "</tr>";
+
+ return $ci;
+ }
+
+ function buildExpiryInput() {
+ $id = 'mwProtect-expiry';
+
+ $ci = "<tr> <td align=\"right\">";
+ $ci .= wfElement( 'label', array (
+ 'id' => "$id-label",
+ 'for' => $id ),
+ wfMsg( 'protectexpiry' ) );
+ $ci .= "</td> <td aligh=\"left\">";
+ $ci .= wfElement( 'input', array(
+ 'size' => 60,
+ 'name' => $id,
+ 'id' => $id,
+ 'value' => $this->mExpiry ) );
+ $ci .= "</td></tr>";
return $ci;
}
*/
function wfSpecialProtectedpages() {
- list( $limit, $offset ) = wfCheckLimits();
+ list( $limit, $offset ) = wfCheckLimits();
- $depp = new ProtectedPagesPage();
+ $depp = new ProtectedPagesPage();
- return $depp->doQuery( $offset, $limit );
+ Title::purgeExpiredRestrictions();
+
+ return $depp->doQuery( $offset, $limit );
}
?>
var $mLatestID; # ID of most recent revision
var $mRestrictions; # Array of groups allowed to edit this article
var $mCascadeRestriction; # Cascade restrictions on this page to included templates and images?
+ var $mRestrictionsExpiry; # When do the restrictions on this page expire?
var $mHasCascadingRestrictions; # Are cascading restrictions in effect on this page?
var $mCascadeRestrictionSources;# Where are the cascading restrictions coming from on this page?
var $mRestrictionsLoaded; # Boolean for initialisation on demand
$this->mOldRestrictions = true;
$this->mCascadeRestriction = false;
+ $this->mRestrictionsExpiry = Block::decodeExpiry('');
}
if ($dbr->numRows( $res) ) {
# Current system - load second to make them override.
+ $now = wfTimestampNow( );
+
while ($row = $dbr->fetchObject( $res ) ) {
# Cycle through all the restrictions.
-
- $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
-
- $this->mCascadeRestriction |= $row->pr_cascade;
+
+ // This code should be refactored, now that it's being used more generally,
+ // But I don't really see any harm in leaving it in Block for now -werdna
+ $this->mRestrictionsExpiry = Block::decodeExpiry( $row->pr_expiry );
+
+ // Only apply the restrictions if they haven't expired!
+ if ( !$this->mRestrictionsExpiry || $this->mRestrictionsExpiry > $now ) {
+ $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
+
+ $this->mCascadeRestriction |= $row->pr_cascade;
+ }
}
}
}
}
+ /**
+ * Purge expired restrictions from the page_restrictions table
+ */
+ static function purgeExpiredRestrictions() {
+ $dbw =& wfGetDB( DB_MASTER );
+
+ // Make sure we don't purge NULL pr_expiry, or we'll empty the pr_restrictions table of older protects.
+ $dbw->delete( 'page_restrictions', array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ), 'pr_expiry IS NOT NULL' ), __METHOD__ );
+ }
+
/**
* Accessor/initialisation for mRestrictions
*
'confirmprotect' => 'Confirm protection',
'protectmoveonly' => 'Protect from moves only',
'protectcomment' => 'Reason for protecting',
+'protectexpiry' => 'Expiry',
+'protect_expiry_invalid' => 'Expiry time invalid.',
'unprotectsub' =>"(Unprotecting \"$1\")",
'confirmunprotecttext' => 'Do you really want to unprotect this page?',
'confirmunprotect' => 'Confirm unprotection',
'protect-level-autoconfirmed' => 'Block unregistered users',
'protect-level-sysop' => 'Sysops only',
'protect-summary-cascade' => 'cascading',
+'protect-expiring' => '(expires $1)',
'protect-cascade' => 'Cascading protection - protect any pages included in this page.',
# restrictions (nouns)