(bug 12650) Make it possible to enter separate expiry times for each restriction...
authorAlex Z <mrzman@users.mediawiki.org>
Sat, 13 Sep 2008 05:33:24 +0000 (05:33 +0000)
committerAlex Z <mrzman@users.mediawiki.org>
Sat, 13 Sep 2008 05:33:24 +0000 (05:33 +0000)
RELEASE-NOTES
includes/Article.php
includes/DefaultSettings.php
includes/LogPage.php
includes/ProtectionForm.php
includes/Title.php
includes/specials/SpecialMovepage.php
languages/messages/MessagesEn.php
skins/common/protect.js

index dff4659..5d202fa 100644 (file)
@@ -124,6 +124,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * (bug 15551) Deletion log excerpt is now shown whenever a user vists a 
   deleted page, even if they are unable to edit it.
 * Added Wantedfiles special pages, allowing users to find image links with no image.
+* (bug 12650) It is now possible to set different expiration times for different
+  restriction types on the protection form.
 
 === Bug fixes in 1.14 ===
 
index 1735c9e..b6033d1 100644 (file)
@@ -1798,7 +1798,7 @@ class Article {
         * @param string $reason
         * @return bool true on success
         */
-       function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) {
+       function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = array() ) {
                global $wgUser, $wgRestrictionTypes, $wgContLang;
 
                $id = $this->mTitle->getArticleID();
@@ -1816,15 +1816,17 @@ class Article {
                # FIXME: Same limitations as described in ProtectionForm.php (line 37);
                # we expect a single selection, but the schema allows otherwise.
                $current = array();
-               foreach( $wgRestrictionTypes as $action )
+               $updated = Article::flattenRestrictions( $limit );
+               $changed = false;
+               foreach( $wgRestrictionTypes as $action ) {
                        $current[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
+                       $changed = ($changed || ($this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action]) );
+               }
 
                $current = Article::flattenRestrictions( $current );
-               $updated = Article::flattenRestrictions( $limit );
 
-               $changed = ( $current != $updated );
+               $changed = ($changed || ( $current != $updated ) );
                $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
-               $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry);
                $protect = ( $updated != '' );
 
                # If nothing's changed, do nothing
@@ -1832,14 +1834,6 @@ class Article {
                        if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
 
                                $dbw = wfGetDB( DB_MASTER );
-
-                               $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
-
-                               $expiry_description = '';
-                               if( $encodedExpiry != 'infinity' ) {
-                                       $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', 
-                                               $wgContLang->timeanddate( $expiry, false, false ) ).')';         
-                               }
                                
                                # Prepare a null revision to be added to the history
                                $modified = $current != '' && $protect;
@@ -1859,7 +1853,6 @@ class Article {
                                                break;
                                        }
                                }
-                               
                                $cascade_description = '';       
                                if( $cascade ) {
                                        $cascade_description = ' ['.wfMsgForContent('protect-summary-cascade').']';      
@@ -1869,10 +1862,23 @@ class Article {
                                        $comment .= ": $reason";
 
                                $editComment = $comment;
-                               if( $protect )
-                                       $editComment .= " [$updated]";
-                               if( $expiry_description && $protect )
-                                       $editComment .= "$expiry_description";
+                               $encodedExpiry = array();
+                               $protect_description = '';
+                               foreach( $limit as $action => $restrictions  ) {
+                                       $encodedExpiry[$action] = Block::encodeExpiry($expiry[$action], $dbw );
+                                       if ($restrictions != '') {
+                                               $protect_description .= "[$action=$restrictions] (";
+                                               if( $encodedExpiry[$action] != 'infinity' ) {
+                                                       $protect_description .= wfMsgForContent( 'protect-expiring',  $wgContLang->timeanddate( $expiry[$action], false, false ) );      
+                                               } else {
+                                                       $protect_description .= wfMsgForContent( 'protect-expiry-indefinite' );
+                                               }
+                                               $protect_description .= ') ';
+                                       }
+                               }
+                                       
+                               if( $protect_description && $protect )
+                                       $editComment .= "($protect_description)";
                                if( $cascade )
                                        $editComment .= "$cascade_description";
                                # Update restrictions table
@@ -1880,8 +1886,8 @@ class Article {
                                        if ($restrictions != '' ) {
                                                $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
                                                        array( 'pr_page' => $id, 'pr_type' => $action
-                                                               , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
-                                                               , 'pr_expiry' => $encodedExpiry ), __METHOD__  );
+                                                               , 'pr_level' => $restrictions, 'pr_cascade' => $cascade && $action == 'edit' ? 1 : 0
+                                                               , 'pr_expiry' => $encodedExpiry[$action] ), __METHOD__  );
                                        } else {
                                                $dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
                                                        'pr_type' => $action ), __METHOD__ );
@@ -1910,7 +1916,7 @@ class Article {
                                # Update the protection log
                                $log = new LogPage( 'protect' );
                                if( $protect ) {
-                                       $params = array($updated,$encodedExpiry,$cascade ? 'cascade' : '');
+                                       $params = array($protect_description,$cascade ? 'cascade' : '');
                                        $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason), $params );
                                } else {
                                        $log->addEntry( 'unprotect', $this->mTitle, $reason );
index 81d5a10..c017c35 100644 (file)
@@ -1383,7 +1383,7 @@ $wgCacheEpoch = '20030516000000';
  * to ensure that client-side caches don't keep obsolete copies of global
  * styles.
  */
-$wgStyleVersion = '176';
+$wgStyleVersion = '177';
 
 
 # Server-side caching:
index 7ace1ac..2b821e9 100644 (file)
@@ -203,13 +203,9 @@ class LogPage {
                                                }
                                                $params[2] = isset( $params[2] ) ? 
                                                        self::formatBlockFlags( $params[2], is_null( $skin ) ) : '';
-                                       } else if ( $type == 'protect' && count($params) == 4 ) {
-                                               $details .= " [{$params[1]}]"; // the restrictions
-                                               if( $params[2] != 'infinity' ) {
-                                                       $details .= ' (' . wfMsgForContent( 'protect-expiring', 
-                                                               $wgContLang->timeanddate( $params[2], false, false ) ).')';
-                                               }
-                                               if( $params[3] ) {
+                                       } else if ( $type == 'protect' && count($params) == 3 ) {
+                                               $details .= " {$params[1]}"; // restrictions and expiries
+                                               if( $params[2] ) {
                                                        $details .= ' ['.wfMsg('protect-summary-cascade').']';
                                                }
                                        }
index 222851c..617b953 100644 (file)
 class ProtectionForm {
        var $mRestrictions = array();
        var $mReason = '';
+       var $mReasonList = '';
        var $mCascade = false;
-       var $mExpiry = null;
+       var $mExpiry =array();
+       var $mExpiryList = array();
        var $mPermErrors = array();
        var $mApplicableTypes = array();
 
@@ -44,18 +46,17 @@ class ProtectionForm {
                                // Fixme: this form currently requires individual selections,
                                // but the db allows multiples separated by commas.
                                $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
+                               
+                               if ( $this->mTitle->mRestrictionsExpiry[$action] == 'infinity' ) {
+                                       $this->mExpiry[$action] = 'infinite';
+                               } else if ( strlen($this->mTitle->mRestrictionsExpiry[$action]) == 0 ) {
+                                       $this->mExpiry[$action] = '';
+                               } else {
+                                       // FIXME: this format is not user friendly
+                                       $this->mExpiry[$action] = wfTimestamp( TS_ISO_8601, $this->mTitle->mRestrictionsExpiry[$action] );
+                               }
                        }
-
                        $this->mCascade = $this->mTitle->areRestrictionsCascading();
-
-                       if ( $this->mTitle->mRestrictionsExpiry == 'infinity' ) {
-                               $this->mExpiry = 'infinite';
-                       } else if ( strlen($this->mTitle->mRestrictionsExpiry) == 0 ) {
-                               $this->mExpiry = '';
-                       } else {
-                               // FIXME: this format is not user friendly
-                               $this->mExpiry = wfTimestamp( TS_ISO_8601, $this->mTitle->mRestrictionsExpiry );
-                       }
                }
 
                // The form will be available in read-only to show levels.
@@ -67,14 +68,15 @@ class ProtectionForm {
                $this->mReason = $wgRequest->getText( 'mwProtect-reason' );
                $this->mReasonList = $wgRequest->getText( 'wpProtectReasonList' );
                $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade', $this->mCascade );
-               // Let dropdown have 'infinite' for unprotected pages
-               if( !($expiry = $wgRequest->getText( 'mwProtect-expiry' )) && $this->mExpiry != 'infinite' ) {
-                       $expiry = $this->mExpiry;
-               }
-               $this->mExpiry = $expiry;
-               $this->mExpiryList = $wgRequest->getText( 'wpProtectExpiryList', $this->mExpiry ? '' : 'infinite' );
-
+               
                foreach( $this->mApplicableTypes as $action ) {
+                       // Let dropdown have 'infinite' for unprotected pages
+                       if( !($expiry[$action] = $wgRequest->getText( "mwProtect-expiry-$action" )) && $this->mExpiry[$action] != 'infinite' ) {
+                               $expiry[$action] = $this->mExpiry[$action];
+                       }
+                       $this->mExpiry[$action] = $expiry[$action];
+                       $this->mExpiryList[$action] = $wgRequest->getText( "wpProtectExpiryList-$action", $this->mExpiry[$action] ? '' : 'infinite' );
+
                        $val = $wgRequest->getVal( "mwProtect-level-$action" );
                        if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) {
                                // Prevent users from setting levels that they cannot later unset
@@ -175,32 +177,35 @@ class ProtectionForm {
                } elseif ( $reasonstr == 'other' ) {
                        $reasonstr = $this->mReason;
                }
-               # Custom expiry takes precedence
-               if ( strlen( $this->mExpiry ) == 0 ) {
-                       $this->mExpiry = strlen($this->mExpiryList) ? $this->mExpiryList : '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 false;
+               $expiry = array();
+               foreach( $this->mApplicableTypes as $action ) {
+                       # Custom expiry takes precedence
+                       if ( strlen( $wgRequest->getText( "mwProtect-expiry-$action" ) ) == 0 ) {
+                               $this->mExpiry[$action] = strlen($wgRequest->getText( "wpProtectExpiryList-$action")) ? $wgRequest->getText( "wpProtectExpiryList-$action") : 'infinite';
+                       } else {
+                               $this->mExpiry[$action] = $wgRequest->getText( "mwProtect-expiry-$action" );
                        }
+                       if ( $this->mExpiry[$action] == 'infinite' || $this->mExpiry[$action] == 'indefinite' ) {
+                               $expiry[$action] = Block::infinity();
+                       } else {
+                               # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1
+                               $expiry[$action] = strtotime( $this->mExpiry[$action] );
+
+                               if ( $expiry[$action] < 0 || $expiry[$action] === false ) {
+                                       $this->show( wfMsg( 'protect_expiry_invalid' ) );
+                                       return false;
+                               }
 
-                       // Fixme: non-qualified absolute times are not in users specified timezone
-                       // and there isn't notice about it in the ui
-                       $expiry = wfTimestamp( TS_MW, $expiry );
+                               // Fixme: non-qualified absolute times are not in users specified timezone
+                               // and there isn't notice about it in the ui
+                               $expiry[$action] = wfTimestamp( TS_MW, $expiry[$action] );
 
-                       if ( $expiry < wfTimestampNow() ) {
-                               $this->show( wfMsg( 'protect_expiry_old' ) );
-                               return false;
+                               if ( $expiry[$action] < wfTimestampNow() ) {
+                                       $this->show( wfMsg( 'protect_expiry_old' ) );
+                                       return false;
+                               }
                        }
                }
-
                # They shouldn't be able to do this anyway, but just to make sure, ensure that cascading restrictions aren't being applied
                #  to a semi-protected page.
                global $wgGroupPermissions;
@@ -214,7 +219,7 @@ class ProtectionForm {
                if ($this->mTitle->exists()) {
                        $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $reasonstr, $this->mCascade, $expiry );
                } else {
-                       $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $reasonstr, $expiry );
+                       $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $reasonstr, $expiry['create'] );
                }
 
                if( !$ok ) {
@@ -238,8 +243,6 @@ class ProtectionForm {
        function buildForm() {
                global $wgUser;
 
-               $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), 'mwProtectExpiryList' );
-               $mProtectother = Xml::label( wfMsg( 'protect-othertime' ), 'expires' );
                $mProtectreasonother = Xml::label( wfMsg( 'protectcomment' ), 'wpProtectReasonList' );
                $mProtectreason = Xml::label( wfMsg( 'protect-otherreason' ), 'mwProtect-reason' );
 
@@ -257,10 +260,9 @@ class ProtectionForm {
                $out .= Xml::openElement( 'fieldset' ) .
                        Xml::element( 'legend', null, wfMsg( 'protect-legend' ) ) .
                        Xml::openElement( 'table', array( 'id' => 'mwProtectSet' ) ) .
-                       Xml::openElement( 'tbody' ) .
-                       "<tr>\n";
+                       Xml::openElement( 'tbody' );
 
-               foreach( $this->mRestrictions as $action => $required ) {
+               foreach( $this->mRestrictions as $action => $selected ) {
                        /* Not all languages have V_x <-> N_x relation */
                        $msg = wfMsg( 'restriction-' . $action );
                        if( wfEmptyMsg( 'restriction-' . $action, $msg ) ) {
@@ -269,32 +271,53 @@ class ProtectionForm {
                        $label = Xml::element( 'label',
                                        array( 'for' => "mwProtect-level-$action" ),
                                        $msg );
-                       $out .= "<th>$label</th>";
-               }
-               $out .= "</tr>
-                       <tr>\n";
-               foreach( $this->mRestrictions as $action => $selected ) {
-                       $out .= "<td>" .
-                                       $this->buildSelector( $action, $selected ) .
-                               "</td>";
-               }
-               $out .= "</tr>\n";
-               
-               $scExpiryOptions = wfMsgForContent( 'ipboptions' ); // FIXME: use its own message
-
-               $showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled);
-               if( !$showProtectOptions )
-                       $mProtectother = $mProtectexpiry;
-
-               $expiryFormOptions = Xml::option( wfMsg( 'protect-othertime-op' ), 'wpProtectExpiryList' );
-               foreach( explode(',', $scExpiryOptions) as $option ) {
-                       if ( strpos($option, ":") === false ) $option = "$option:$option";
-                       list($show, $value) = explode(":", $option);
-                       $show = htmlspecialchars($show);
-                       $value = htmlspecialchars($value);
-                       $expiryFormOptions .= Xml::option( $show, $value, $this->mExpiryList === $value ? true : false ) . "\n";
+                       $out .= "<tr><th>$label</th></tr>";
+                       $out .= "<tr><td>" .
+                       $this->buildSelector( $action, $selected ) .
+                               "</td></tr>";
+                       $scExpiryOptions = wfMsgForContent( 'ipboptions' ); // FIXME: use its own message
+
+                       $showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled);
+                       
+                       $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), "mwProtectExpiryList-$action" );
+                       $mProtectother = Xml::label( wfMsg( 'protect-othertime' ), "mwProtect-$action-expires" );
+                       $expiryFormOptions = Xml::option( wfMsg( 'protect-othertime-op' ), "wpProtectExpiryList-$action" );
+                       foreach( explode(',', $scExpiryOptions) as $option ) {
+                               if ( strpos($option, ":") === false ) $option = "$option:$option";
+                               list($show, $value) = explode(":", $option);
+                               $show = htmlspecialchars($show);
+                               $value = htmlspecialchars($value);
+                               $expiryFormOptions .= Xml::option( $show, $value, $this->mExpiryList[$action] === $value ? true : false ) . "\n";
+                       }
+                       # Add expiry dropdown
+                       if( $showProtectOptions && !$this->disabled ) {
+                               $out .= "
+                                       <tr>
+                                               <td class='mw-label'>
+                                                       {$mProtectexpiry}
+                                               </td>
+                                               <td class='mw-input'>" .
+                                                       Xml::tags( 'select',
+                                                               array(
+                                                                       'id' => "mwProtectExpiryList-$action",
+                                                                       'name' => "wpProtectExpiryList-$action",
+                                                                       'onchange' => "protectExpiryListUpdate(this)",
+                                                                       'tabindex' => '2' ) + $this->disabledAttrib,
+                                                               $expiryFormOptions ) .
+                                               "</td>
+                                       </tr>";
+                       }
+                       # Add custom expiry field
+                       $attribs = array( 'id' => "mwProtect-$action-expires", 'onkeyup' => 'protectExpiryUpdate(this)' ) + $this->disabledAttrib;
+                       $out .= "<tr>
+                                       <td class='mw-label'>" .
+                                               $mProtectother .
+                                       '</td>
+                                       <td class="mw-input">' .
+                                               Xml::input( "mwProtect-expiry-$action", 60, $this->mExpiry[$action], $attribs ) .
+                                       '</td>
+                               </tr>';
                }
-               
                $reasonDropDown = Xml::listDropDown( 'wpProtectReasonList',
                        wfMsgForContent( 'protect-dropdown' ),
                        wfMsgForContent( 'protect-otherreason-op' ), '', 'mwProtect-reason', 4 );
@@ -314,34 +337,6 @@ class ProtectionForm {
                                        "</td>
                                </tr>\n";
                }
-               # Add expiry dropdown
-               if( $showProtectOptions && !$this->disabled ) {
-                       $out .= "
-                               <tr>
-                                       <td class='mw-label'>
-                                               {$mProtectexpiry}
-                                       </td>
-                                       <td class='mw-input'>" .
-                                               Xml::tags( 'select',
-                                                       array(
-                                                               'id' => 'mwProtectExpiryList',
-                                                               'name' => 'wpProtectExpiryList',
-                                                               'onchange' => "document.getElementById('expires').value='';",
-                                                               'tabindex' => '2' ) + $this->disabledAttrib,
-                                                       $expiryFormOptions ) .
-                                       "</td>
-                               </tr>";
-               }
-               # Add custom expiry field
-               $attribs = array( 'id' => 'expires' ) + $this->disabledAttrib;
-               $out .= "<tr>
-                               <td class='mw-label'>" .
-                                       $mProtectother .
-                               '</td>
-                               <td class="mw-input">' .
-                                       Xml::input( 'mwProtect-expiry', 60, $this->mExpiry, $attribs ) .
-                               '</td>
-                       </tr>';
                # Add manual and custom reason field/selects
                if( !$this->disabled ) {
                        $out .= "
index fae7775..209d2f1 100644 (file)
@@ -56,7 +56,7 @@ class Title {
        var $mRestrictions = array();     ///< Array of groups allowed to edit this article
        var $mOldRestrictions = false;
        var $mCascadeRestriction;         ///< Cascade restrictions on this page to included templates and images?
-       var $mRestrictionsExpiry;         ///< When do the restrictions on this page expire?
+       var $mRestrictionsExpiry = array();       ///< When do the restrictions on this page expire?
        var $mHasCascadingRestrictions;   ///< Are cascading restrictions in effect on this page?
        var $mCascadeSources;  ///< Where are the cascading restrictions coming from on this page?
        var $mRestrictionsLoaded = false; ///< Boolean for initialisation on demand
@@ -1368,7 +1368,7 @@ class Title {
                global $wgUser,$wgContLang;
 
                if ($create_perm == implode(',',$this->getRestrictions('create'))
-                       && $expiry == $this->mRestrictionsExpiry) {
+                       && $expiry == $this->mRestrictionsExpiry['create']) {
                        // No change
                        return true;
                }
@@ -1383,7 +1383,10 @@ class Title {
                if ( $encodedExpiry != 'infinity' ) {
                        $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
                }
-
+               else {
+                       $expiry_description .= ' (' . wfMsgForContent( 'protect-expiry-indefinite' ).')';
+               }
+       
                # Update protection table
                if ($create_perm != '' ) {
                        $dbw->replace( 'protected_titles', array(array('pt_namespace', 'pt_title')),
@@ -1740,7 +1743,6 @@ class Title {
                } else {
                        $this->mHasCascadingRestrictions = $sources;
                }
-
                return array( $sources, $pagerestrictions );
        }
 
@@ -1762,10 +1764,10 @@ class Title {
 
                foreach( $wgRestrictionTypes as $type ){
                        $this->mRestrictions[$type] = array();
+                       $this->mRestrictionsExpiry[$type] = Block::decodeExpiry('');
                }
 
                $this->mCascadeRestriction = false;
-               $this->mRestrictionsExpiry = Block::decodeExpiry('');
 
                # Backwards-compatibility: also load the restrictions from the page record (old format).
 
@@ -1809,7 +1811,7 @@ class Title {
 
                                // Only apply the restrictions if they haven't expired!
                                if ( !$expiry || $expiry > $now ) {
-                                       $this->mRestrictionsExpiry = $expiry;
+                                       $this->mRestrictionsExpiry[$row->pr_type] = $expiry;
                                        $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) );
 
                                        $this->mCascadeRestriction |= $row->pr_cascade;
@@ -1850,13 +1852,13 @@ class Title {
 
                                        if (!$expiry || $expiry > $now) {
                                                // Apply the restrictions
-                                               $this->mRestrictionsExpiry = $expiry;
+                                               $this->mRestrictionsExpiry['create'] = $expiry;
                                                $this->mRestrictions['create'] = explode(',', trim($pt_create_perm) );
                                        } else { // Get rid of the old restrictions
                                                Title::purgeExpiredRestrictions();
                                        }
                                } else {
-                                       $this->mRestrictionsExpiry = Block::decodeExpiry('');
+                                       $this->mRestrictionsExpiry['create'] = Block::decodeExpiry('');
                                }
                                $this->mRestrictionsLoaded = true;
                        }
index 4f3ae34..a912f84 100644 (file)
@@ -292,6 +292,10 @@ class MovePageForm {
 
                $error = $ot->moveTo( $nt, true, $this->reason );
                if ( $error !== true ) {
+                       if (isset($error[0][0]) && $error[0][0] = 'cascadeprotected') {
+                               $wgOut->showPermissionsErrorPage($error, 'move');
+                               return;
+                       }
                        # FIXME: showForm() should handle multiple errors
                        call_user_func_array(array($this, 'showForm'), $error[0]);
                        return;
index f103509..859e6c6 100644 (file)
@@ -2315,6 +2315,7 @@ You can change this page's protection level, but it will not affect the cascadin
 'protect-level-sysop'         => 'Sysops only',
 'protect-summary-cascade'     => 'cascading',
 'protect-expiring'            => 'expires $1 (UTC)',
+'protect-expiry-indefinite' => 'indefinite',
 'protect-cascade'             => 'Protect pages included in this page (cascading protection)',
 'protect-cantedit'            => 'You cannot change the protection levels of this page, because you do not have permission to edit it.',
 'protect-othertime'           => 'Other time:',
index 863b95b..7541533 100644 (file)
@@ -100,6 +100,40 @@ function protectLevelsUpdate(source) {
        setCascadeCheckbox();
 }
 
+/**
+ * When protection levels are locked together, update the 
+ * expiries when one changes
+ *
+ * @param Element source expiry input that changed
+ */
+
+function protectExpiryUpdate(source) {
+       if( !protectUnchained() ) {
+               var expiry = source.value;
+               expiryForInputs(function(set) {
+                       set.value = expiry;
+               });
+       }
+}
+
+/**
+ * When protection levels are locked together, update the 
+ * expiry lists when one changes and clear the custom inputs
+ *
+ * @param Element source expiry selector that changed
+ */
+function protectExpiryListUpdate(source) {
+       if( !protectUnchained() ) {
+               var expiry = source.value;
+               expiryListForInputs(function(set) {
+                       set.value = expiry;
+               });
+               expiryForInputs(function(set) {
+                       set.value = '';
+               });
+       }
+}
+
 /**
  * Update chain status and enable/disable various bits of the UI
  * when the user changes the "unlock move permissions" checkbox
@@ -200,7 +234,64 @@ function protectSelectors() {
 }
 
 /**
- * Enable/disable protection selectors
+ * Apply a callback to each expiry input
+ *
+ * @param callable func Callback function
+ */
+function expiryForInputs(func) {
+       var inputs = expiryInputs();
+       for (var i = 0; i < inputs.length; i++) {
+               func(inputs[i]);
+       }
+}
+
+/**
+ * Get a list of all expiry inputs on the page
+ *
+ * @return Array
+ */
+function expiryInputs() {
+       var all = document.getElementsByTagName("input");
+       var ours = new Array();
+       for (var i = 0; i < all.length; i++) {
+               var set = all[i];
+               if (set.name.match(/^mwProtect-expiry-/)) {
+                       ours[ours.length] = set;
+               }
+       }
+       return ours;
+}
+
+/**
+ * Apply a callback to each expiry selector list
+ * @param callable func Callback function
+ */
+function expiryListForInputs(func) {
+       var inputs = expiryListInputs();
+       for (var i = 0; i < inputs.length; i++) {
+               func(inputs[i]);
+       }
+}
+
+/**
+ * Get a list of all expiry selector lists on the page
+ *
+ * @return Array
+ */
+function expiryListInputs() {
+       var all = document.getElementsByTagName("select");
+       var ours = new Array();
+       for (var i = 0; i < all.length; i++) {
+               var set = all[i];
+               if (set.id.match(/^mwProtectExpiryList-/)) {
+                       ours[ours.length] = set;
+               }
+       }
+       return ours;
+}
+
+/**
+ * Enable/disable protection selectors and expiry inputs
  *
  * @param boolean val Enable?
  */
@@ -215,4 +306,22 @@ function protectEnable(val) {
                        set.style.visible = val ? "visible" : "hidden";
                }
        });
+       first = true;
+       expiryForInputs(function(set) {
+               if (first) {
+                       first = false;
+               } else {
+                       set.disabled = !val;
+                       set.style.visible = val ? "visible" : "hidden";
+               }
+       });
+       first = true;
+       expiryListForInputs(function(set) {
+               if (first) {
+                       first = false;
+               } else {
+                       set.disabled = !val;
+                       set.style.visible = val ? "visible" : "hidden";
+               }
+       });
 }