From 957839573b26bba978f523e38f4c9aa0dbc49366 Mon Sep 17 00:00:00 2001 From: Andrew Garrett Date: Mon, 22 Jan 2007 08:26:41 +0000 Subject: [PATCH] * (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. --- RELEASE-NOTES | 3 + includes/Article.php | 18 +++++- includes/ProtectionForm.php | 89 +++++++++++++++++++++++++----- includes/SpecialProtectedpages.php | 8 ++- includes/Title.php | 29 ++++++++-- languages/messages/MessagesEn.php | 3 + 6 files changed, 127 insertions(+), 23 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index c59558d3d9..115d198534 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -136,6 +136,9 @@ lighter making things easier to read. 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 == diff --git a/includes/Article.php b/includes/Article.php index 8c5b19d8cb..20eb4ef84f 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1651,7 +1651,7 @@ class Article { * @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(); @@ -1663,6 +1663,9 @@ class Article { $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(); @@ -1674,6 +1677,7 @@ class Article { $changed = ( $current != $updated ); $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade); + $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry); $protect = ( $updated != '' ); # If nothing's changed, do nothing @@ -1693,10 +1697,13 @@ class Article { # 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__ ); @@ -1719,13 +1726,18 @@ class Article { $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 ); } diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php index 04b95bd7ab..e349fba44b 100644 --- a/includes/ProtectionForm.php +++ b/includes/ProtectionForm.php @@ -25,14 +25,17 @@ class ProtectionForm { 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. @@ -40,6 +43,14 @@ class ProtectionForm { } $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. @@ -51,16 +62,24 @@ class ProtectionForm { 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' ); @@ -74,6 +93,11 @@ class ProtectionForm { $cascadeSources = $this->mTitle->getCascadeProtectionSources(); + if ( "" != $err ) { + $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) ); + $wgOut->addHTML( "

{$err}

\n" ); + } + if ( $cascadeSources && count($cascadeSources) > 0 ) { $titles = ''; @@ -86,11 +110,6 @@ class ProtectionForm { $wgOut->addWikiText( $notice ); } - if( $this->save() ) { - $wgOut->redirect( $this->mTitle->getFullUrl() ); - return; - } - $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); $wgOut->setSubtitle( wfMsg( 'protectsub', $this->mTitle->getPrefixedText() ) ); @@ -118,7 +137,25 @@ class ProtectionForm { 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." ); } @@ -165,18 +202,25 @@ class ProtectionForm { $out .= "\n"; $out .= "\n"; + $out .= "\n"; + $out .= "\n"; + global $wgEnableCascadingProtection; if ($wgEnableCascadingProtection) $out .= $this->buildCascadeInput(); + $out .= $this->buildExpiryInput(); + if( !$this->disabled ) { - $out .= "
\n"; - $out .= "\n"; $out .= "\n"; $out .= "\n"; - $out .= "\n"; - $out .= "
" . $this->buildReasonInput() . "
" . $this->buildSubmit() . "
\n"; + } + + $out .= "\n"; + $out .= "\n"; + + if ( !$this->disabled ) { $out .= "\n"; $out .= $this->buildCleanupScript(); } @@ -229,7 +273,26 @@ class ProtectionForm { function buildCascadeInput() { $id = 'mwProtect-cascade'; - $ci = wfCheckLabel( wfMsg( 'protect-cascade' ), $id, $id, $this->mCascade, $this->disabledAttrib); + $ci = "" . wfCheckLabel( wfMsg( 'protect-cascade' ), $id, $id, $this->mCascade, $this->disabledAttrib) . ""; + + return $ci; + } + + function buildExpiryInput() { + $id = 'mwProtect-expiry'; + + $ci = " "; + $ci .= wfElement( 'label', array ( + 'id' => "$id-label", + 'for' => $id ), + wfMsg( 'protectexpiry' ) ); + $ci .= " "; + $ci .= wfElement( 'input', array( + 'size' => 60, + 'name' => $id, + 'id' => $id, + 'value' => $this->mExpiry ) ); + $ci .= ""; return $ci; } diff --git a/includes/SpecialProtectedpages.php b/includes/SpecialProtectedpages.php index a3674c0c8a..b66f94e250 100644 --- a/includes/SpecialProtectedpages.php +++ b/includes/SpecialProtectedpages.php @@ -70,11 +70,13 @@ class ProtectedPagesPage extends PageQueryPage { */ 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 ); } ?> diff --git a/includes/Title.php b/includes/Title.php index 62080d87b2..bc5af31179 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -52,6 +52,7 @@ class Title { 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 @@ -1471,17 +1472,27 @@ class Title { $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; + } } } @@ -1499,6 +1510,16 @@ class Title { } } + /** + * 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 * diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 4f8b70d1e5..7d97bfb26f 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -1746,6 +1746,8 @@ Please hit "back" and reload the page you came from, then try again.', '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', @@ -1759,6 +1761,7 @@ page protection levels. Here are the current settings for the page $1 '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) -- 2.20.1