* @todo document, briefly.
*/
class ProtectionForm {
+ /** A map of action to restriction level, from request or default */
var $mRestrictions = array();
+
+ /** The custom/additional protection reason */
var $mReason = '';
- var $mReasonList = '';
+
+ /** The reason selected from the list, blank for other/additional */
+ var $mReasonSelection = '';
+
+ /** True if the restrictions are cascading, from request or existing protection */
var $mCascade = false;
- var $mExpiry =array();
- var $mExpiryList = array();
+
+ /** Map of action to "other" expiry time. Used in preference to mExpirySelection. */
+ var $mExpiry = array();
+
+ /**
+ * Map of action to value selected in expiry drop-down list.
+ * Will be set to 'othertime' whenever mExpiry is set.
+ */
+ var $mExpirySelection = array();
+
+ /** Permissions errors for the protect action */
var $mPermErrors = array();
+
+ /** Types (i.e. actions) for which levels can be selected */
var $mApplicableTypes = array();
+ /** Map of action to the expiry time of the existing protection */
+ var $mExistingExpiry = array();
+
function __construct( Article $article ) {
global $wgRequest, $wgUser;
global $wgRestrictionTypes, $wgRestrictionLevels;
$this->mTitle = $article->mTitle;
$this->mApplicableTypes = $this->mTitle->exists() ? $wgRestrictionTypes : array('create');
- if( $this->mTitle ) {
- $this->mTitle->loadRestrictions();
-
- 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 ) );
-
- 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();
- }
+ $this->mCascade = $this->mTitle->areRestrictionsCascading();
// The form will be available in read-only to show levels.
- $this->disabled = wfReadOnly() || ($this->mPermErrors = $this->mTitle->getUserPermissionsErrors('protect',$wgUser)) != array();
+ $this->mPermErrors = $this->mTitle->getUserPermissionsErrors('protect',$wgUser);
+ $this->disabled = wfReadOnly() || $this->mPermErrors != array();
$this->disabledAttrib = $this->disabled
? array( 'disabled' => 'disabled' )
: array();
$this->mReason = $wgRequest->getText( 'mwProtect-reason' );
- $this->mReasonList = $wgRequest->getText( 'wpProtectReasonList' );
+ $this->mReasonSelection = $wgRequest->getText( 'wpProtectReasonSelection' );
$this->mCascade = $wgRequest->getBool( 'mwProtect-cascade', $this->mCascade );
-
+
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];
+ // 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->mRestrictions[$action] ) {
+ // No existing expiry
+ $existingExpiry = '';
+ } else {
+ $existingExpiry = $this->mTitle->getRestrictionExpiry( $action );
+ }
+ $this->mExistingExpiry[$action] = $existingExpiry;
+
+ $requestExpiry = $wgRequest->getText( "mwProtect-expiry-$action" );
+ $requestExpirySelection = $wgRequest->getVal( "wpProtectExpirySelection-$action" );
+
+ if ( $requestExpiry ) {
+ // Custom expiry takes precedence
+ $this->mExpiry[$action] = $requestExpiry;
+ $this->mExpirySelection[$action] = 'othertime';
+ } elseif ( $requestExpirySelection ) {
+ // Expiry selected from list
+ $this->mExpiry[$action] = '';
+ $this->mExpirySelection[$action] = $requestExpirySelection;
+ } elseif ( $existingExpiry == 'infinite' ) {
+ // Existing expiry is infinite, use "infinite" in drop-down
+ $this->mExpiry[$action] = '';
+ $this->mExpirySelection[$action] = 'infinite';
+ } elseif ( $existingExpiry ) {
+ // Use existing expiry in its own list item
+ $this->mExpiry[$action] = '';
+ $this->mExpirySelection[$action] = $existingExpiry;
+ } else {
+ // Final default: infinite
+ $this->mExpiry[$action] = '';
+ $this->mExpirySelection[$action] = 'infinite';
}
- $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 ) ) {
}
}
+ /**
+ * Get the expiry time for a given action, by combining the relevant inputs.
+ * Returns a 14-char timestamp or "infinity", or false if the input was invalid
+ */
+ function getExpiry( $action ) {
+ if ( $this->mExpirySelection[$action] == 'existing' ) {
+ return $this->mExistingExpiry[$action];
+ } elseif ( $this->mExpirySelection[$action] == 'othertime' ) {
+ $value = $this->mExpiry[$action];
+ } else {
+ $value = $this->mExpirySelection[$action];
+ }
+ if ( $value == 'infinite' || $value == 'indefinite' || $value == 'infinity' ) {
+ $time = Block::infinity();
+ } else {
+ $unix = strtotime( $value );
+
+ if ( !$unix || $unix === -1 ) {
+ return false;
+ }
+
+ // Fixme: non-qualified absolute times are not in users specified timezone
+ // and there isn't notice about it in the ui
+ $time = wfTimestamp( TS_MW, $unix );
+ }
+ return $time;
+ }
+
function execute() {
global $wgRequest, $wgOut;
if( $wgRequest->wasPosted() ) {
}
# Create reason string. Use list and/or custom string.
- $reasonstr = $this->mReasonList;
+ $reasonstr = $this->mReasonSelection;
if ( $reasonstr != 'other' && $this->mReason != '' ) {
// Entry from drop down menu + additional comment
$reasonstr .= ': ' . $this->mReason;
}
$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" );
+ $expiry[$action] = $this->getExpiry( $action );
+ if ( !$expiry[$action] ) {
+ $this->show( wfMsg( 'protect_expiry_invalid' ) );
+ return false;
}
- 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[$action] = wfTimestamp( TS_MW, $expiry[$action] );
-
- if ( $expiry[$action] < 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;
} elseif( $this->mTitle->userIsWatching() ) {
$this->mArticle->doUnwatch();
}
-
return $ok;
}
* @return $out string HTML form
*/
function buildForm() {
- global $wgUser;
+ global $wgUser, $wgLang;
- $mProtectreasonother = Xml::label( wfMsg( 'protectcomment' ), 'wpProtectReasonList' );
+ $mProtectreasonother = Xml::label( wfMsg( 'protectcomment' ), 'wpProtectReasonSelection' );
$mProtectreason = Xml::label( wfMsg( 'protect-otherreason' ), 'mwProtect-reason' );
$out = '';
if( !$this->disabled ) {
$out .= $this->buildScript();
- // The submission needs to reenable the move permission selector
- // if it's in locked mode, or some browsers won't submit the data.
$out .= Xml::openElement( 'form', array( 'method' => 'post',
'action' => $this->mTitle->getLocalUrl( 'action=protect' ),
- 'id' => 'mw-Protect-Form', 'onsubmit' => 'protectEnable(true)' ) );
+ 'id' => 'mw-Protect-Form', 'onsubmit' => 'ProtectionForm.enableUnchainedInputs(true)' ) );
$out .= Xml::hidden( 'wpEditToken',$wgUser->editToken() );
}
"<tr><th>$label</th><th></th></tr>" .
"<tr><td>" . $this->buildSelector( $action, $selected ) . "</td><td>";
- $reasonDropDown = Xml::listDropDown( 'wpProtectReasonList',
+ $reasonDropDown = Xml::listDropDown( 'wpProtectReasonSelection',
wfMsgForContent( 'protect-dropdown' ),
- wfMsgForContent( 'protect-otherreason-op' ), '', 'mwProtect-reason', 4 );
+ wfMsgForContent( 'protect-otherreason-op' ),
+ $this->mReasonSelection,
+ 'mwProtect-reason', 4 );
$scExpiryOptions = wfMsgForContent( 'ipboptions' ); // FIXME: use its own message
$showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled);
- $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), "mwProtectExpiryList-$action" );
+ $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), "mwProtectExpirySelection-$action" );
$mProtectother = Xml::label( wfMsg( 'protect-othertime' ), "mwProtect-$action-expires" );
- $expiryFormOptions = Xml::option( wfMsg( 'protect-othertime-op' ), "othertime" );
+
+ $expiryFormOptions = '';
+ if ( $this->mExistingExpiry[$action] && $this->mExistingExpiry[$action] != 'infinity' ) {
+ $expiryFormOptions .=
+ Xml::option(
+ wfMsg( 'protect-existing-expiry', $wgLang->timeanddate( $this->mExistingExpiry[$action] ) ),
+ 'existing',
+ $this->mExpirySelection[$action] == 'existing'
+ ) . "\n";
+ }
+
+ $expiryFormOptions .= Xml::option( wfMsg( 'protect-othertime-op' ), "othertime" ) . "\n";
foreach( explode(',', $scExpiryOptions) as $option ) {
- if ( strpos($option, ":") === false ) $option = "$option:$option";
- list($show, $value) = explode(":", $option);
+ if ( strpos($option, ":") === false ) {
+ $show = $value = $option;
+ } else {
+ list($show, $value) = explode(":", $option);
+ }
$show = htmlspecialchars($show);
$value = htmlspecialchars($value);
- $expiryFormOptions .= Xml::option( $show, $value, $this->mExpiryList[$action] === $value ? true : false ) . "\n";
+ $expiryFormOptions .= Xml::option( $show, $value, $this->mExpirySelection[$action] === $value ) . "\n";
}
# Add expiry dropdown
if( $showProtectOptions && !$this->disabled ) {
<td class='mw-input'>" .
Xml::tags( 'select',
array(
- 'id' => "mwProtectExpiryList-$action",
- 'name' => "wpProtectExpiryList-$action",
- 'onchange' => "protectExpiryListUpdate(this)",
+ 'id' => "mwProtectExpirySelection-$action",
+ 'name' => "wpProtectExpirySelection-$action",
+ 'onchange' => "ProtectionForm.updateExpiryList(this)",
'tabindex' => '2' ) + $this->disabledAttrib,
$expiryFormOptions ) .
"</td>
</tr></table>";
}
# Add custom expiry field
- $attribs = array( 'id' => "mwProtect-$action-expires", 'onkeyup' => 'protectExpiryUpdate(this)' ) + $this->disabledAttrib;
+ $attribs = array( 'id' => "mwProtect-$action-expires", 'onkeyup' => 'ProtectionForm.updateExpiry(this)' ) + $this->disabledAttrib;
$out .= "<table><tr>
<td class='mw-label'>" .
$mProtectother .
'id' => $id,
'name' => $id,
'size' => count( $levels ),
- 'onchange' => 'protectLevelsUpdate(this)',
+ 'onchange' => 'ProtectionForm.updateLevels(this)',
) + $this->disabledAttrib;
$out = Xml::openElement( 'select', $attribs );
global $wgStylePath, $wgStyleVersion;
return Xml::tags( 'script', array(
'type' => 'text/javascript',
- 'src' => $wgStylePath . "/common/protect.js?$wgStyleVersion" ), '' );
+ 'src' => $wgStylePath . "/common/protect.js?$wgStyleVersion.1" ), '' );
}
function buildCleanupScript() {
}
}
$script .= "[" . implode(',',$CascadeableLevels) . "];\n";
- $script .= 'protectInitialize("mw-protect-table2","' . Xml::escapeJsString( wfMsg( 'protect-unchain' ) ) .
- '","' . count($this->mApplicableTypes) . '")';
+ $options = (object)array(
+ 'tableId' => 'mw-protect-table2',
+ 'labelText' => wfMsg( 'protect-unchain' ),
+ 'numTypes' => count($this->mApplicableTypes),
+ 'existingMatch' => 1 == count( array_unique( $this->mExistingExpiry ) ),
+ );
+ $encOptions = Xml::encodeJsVar( $options );
+
+ $script .= "ProtectionForm.init($encOptions)";
return Xml::tags( 'script', array( 'type' => 'text/javascript' ), $script );
}
-/**
- * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
- * on the protection form
- *
- * @param String tableId Identifier of the table containing UI bits
- * @param String labelText Text to use for the checkbox label
- */
-function protectInitialize( tableId, labelText, types ) {
- if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
- return false;
-
- var box = document.getElementById( tableId );
- if( !box )
- return false;
-
- var tbody = box.getElementsByTagName( 'tbody' )[0];
- var row = document.createElement( 'tr' );
- tbody.appendChild( row );
-
- row.appendChild( document.createElement( 'td' ) );
- var col = document.createElement( 'td' );
- row.appendChild( col );
- // If there is only one protection type, there is nothing to chain
- if( types > 1 ) {
- var check = document.createElement( 'input' );
- check.id = 'mwProtectUnchained';
- check.type = 'checkbox';
- col.appendChild( check );
- addClickHandler( check, protectChainUpdate );
- col.appendChild( document.createTextNode( ' ' ) );
- var label = document.createElement( 'label' );
- label.htmlFor = 'mwProtectUnchained';
- label.appendChild( document.createTextNode( labelText ) );
- col.appendChild( label );
+var ProtectionForm = {
+ 'existingMatch': false,
- check.checked = !protectAllMatch();
- protectEnable( check.checked );
- }
-
- setCascadeCheckbox();
-
- return true;
-}
+ /**
+ * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
+ * on the protection form
+ *
+ * @param Object opts : parameters with members:
+ * tableId Identifier of the table containing UI bits
+ * labelText Text to use for the checkbox label
+ * numTypes The number of protection types
+ * existingMatch True if all the existing expiry times match
+ */
+ 'init': function( opts ) {
+ if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
+ return false;
-/**
-* Determine if, given the cascadeable protection levels
-* and what is currently selected, if the cascade box
-* can be checked
-*
-* @return boolean
-*
-*/
-function setCascadeCheckbox() {
- // For non-existent titles, there is no cascade option
- if( !document.getElementById( 'mwProtect-cascade' ) ) {
- return false;
- }
- var lists = protectSelectors();
- for( var i = 0; i < lists.length; i++ ) {
- if( lists[i].selectedIndex > -1 ) {
- var items = lists[i].getElementsByTagName( 'option' );
- var selected = items[ lists[i].selectedIndex ].value;
- if( !isCascadeableLevel(selected) ) {
- document.getElementById( 'mwProtect-cascade' ).checked = false;
- document.getElementById( 'mwProtect-cascade' ).disabled = true;
- return false;
- }
- }
- }
- document.getElementById( 'mwProtect-cascade' ).disabled = false;
- return true;
-}
+ var box = document.getElementById( opts.tableId );
+ if( !box )
+ return false;
-/**
-* Is this protection level cascadeable?
-* @param String level
-*
-* @return boolean
-*
-*/
-function isCascadeableLevel( level ) {
- for (var k = 0; k < wgCascadeableLevels.length; k++) {
- if ( wgCascadeableLevels[k] == level ) {
- return true;
- }
- }
- return false;
-}
+ var tbody = box.getElementsByTagName( 'tbody' )[0];
+ var row = document.createElement( 'tr' );
+ tbody.appendChild( row );
-/**
- * When protection levels are locked together, update the rest
- * when one action's level changes
- *
- * @param Element source Level selector that changed
- */
-function protectLevelsUpdate(source) {
- if( !protectUnchained() )
- protectUpdateAll( source.selectedIndex );
- setCascadeCheckbox();
-}
+ this.existingMatch = opts.existingMatch;
-/**
- * When protection levels are locked together, update the
- * expiries when one changes
- *
- * @param Element source expiry input that changed
- */
+ row.appendChild( document.createElement( 'td' ) );
+ var cell = document.createElement( 'td' );
+ row.appendChild( cell );
+ // If there is only one protection type, there is nothing to chain
+ if( opts.numTypes > 1 ) {
+ var check = document.createElement( 'input' );
+ check.id = 'mwProtectUnchained';
+ check.type = 'checkbox';
+ cell.appendChild( check );
+ addClickHandler( check, function() { ProtectionForm.onChainClick(); } );
-function protectExpiryUpdate(source) {
- if( !protectUnchained() ) {
- var expiry = source.value;
- expiryForInputs(function(set) {
- set.value = expiry;
- });
- }
-}
+ cell.appendChild( document.createTextNode( ' ' ) );
+ var label = document.createElement( 'label' );
+ label.htmlFor = 'mwProtectUnchained';
+ label.appendChild( document.createTextNode( opts.labelText ) );
+ cell.appendChild( label );
-/**
- * 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 = '';
- });
- }
-}
+ check.checked = !this.areAllTypesMatching();
+ this.enableUnchainedInputs( check.checked );
+ }
-/**
- * Update chain status and enable/disable various bits of the UI
- * when the user changes the "unlock move permissions" checkbox
- */
-function protectChainUpdate() {
- if( protectUnchained() ) {
- protectEnable( true );
- } else {
- protectChain();
- protectEnable( false );
- }
- setCascadeCheckbox();
-}
+ this.updateCascadeCheckbox();
-/**
- * Are all actions protected at the same level?
- *
- * @return boolean
- */
-function protectAllMatch() {
- var values = new Array();
- protectForSelectors(function(set) {
- values[values.length] = set.selectedIndex;
- });
- for (var i = 1; i < values.length; i++) {
- if (values[i] != values[0]) {
- return false;
+ return true;
+ },
+
+ /**
+ * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
+ */
+ 'updateCascadeCheckbox': function() {
+ // For non-existent titles, there is no cascade option
+ if( !document.getElementById( 'mwProtect-cascade' ) ) {
+ return;
}
- }
- return true;
-}
+ var lists = this.getLevelSelectors();
+ for( var i = 0; i < lists.length; i++ ) {
+ if( lists[i].selectedIndex > -1 ) {
+ var items = lists[i].getElementsByTagName( 'option' );
+ var selected = items[ lists[i].selectedIndex ].value;
+ if( !this.isCascadeableLevel(selected) ) {
+ document.getElementById( 'mwProtect-cascade' ).checked = false;
+ document.getElementById( 'mwProtect-cascade' ).disabled = true;
+ return;
+ }
+ }
+ }
+ document.getElementById( 'mwProtect-cascade' ).disabled = false;
+ },
-/**
- * Is protection chaining on or off?
- *
- * @return bool
- */
-function protectUnchained() {
- var unchain = document.getElementById( 'mwProtectUnchained' );
- return unchain
- ? unchain.checked
- : true; // No control, so we need to let the user set both levels
-}
+ /**
+ * Is this protection level cascadeable?
+ * @param String level
+ *
+ * @return boolean
+ *
+ */
+ 'isCascadeableLevel': function( level ) {
+ for (var k = 0; k < wgCascadeableLevels.length; k++) {
+ if ( wgCascadeableLevels[k] == level ) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * When protection levels are locked together, update the rest
+ * when one action's level changes
+ *
+ * @param Element source Level selector that changed
+ */
+ 'updateLevels': function(source) {
+ if( !this.isUnchained() )
+ this.setAllSelectors( source.selectedIndex );
+ this.updateCascadeCheckbox();
+ },
-/**
- * Find the highest-protected action and set all others to that level
- */
-function protectChain() {
- var maxIndex = -1;
- protectForSelectors(function(set) {
- if (set.selectedIndex > maxIndex) {
- maxIndex = set.selectedIndex;
+ /**
+ * When protection levels are locked together, update the
+ * expiries when one changes
+ *
+ * @param Element source expiry input that changed
+ */
+
+ 'updateExpiry': function(source) {
+ if( !this.isUnchained() ) {
+ var expiry = source.value;
+ this.forEachExpiryInput(function(element) {
+ element.value = expiry;
+ });
}
- });
- protectUpdateAll(maxIndex);
-}
+ var listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
+ var list = document.getElementById( listId );
+ if (list && list.value != 'othertime' ) {
+ if ( this.isUnchained() ) {
+ list.value = 'othertime';
+ } else {
+ this.forEachExpirySelector(function(element) {
+ element.value = 'othertime';
+ });
+ }
+ }
+ },
-/**
- * Protect all actions at the specified level
- *
- * @param int index Protection level
- */
-function protectUpdateAll(index) {
- protectForSelectors(function(set) {
- if (set.selectedIndex != index) {
- set.selectedIndex = index;
+ /**
+ * 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
+ */
+ 'updateExpiryList': function(source) {
+ if( !this.isUnchained() ) {
+ var expiry = source.value;
+ this.forEachExpirySelector(function(element) {
+ element.value = expiry;
+ });
+ this.forEachExpiryInput(function(element) {
+ element.value = '';
+ });
}
- });
-}
+ },
-/**
- * Apply a callback to each protection selector
- *
- * @param callable func Callback function
- */
-function protectForSelectors(func) {
- var selectors = protectSelectors();
- for (var i = 0; i < selectors.length; i++) {
- func(selectors[i]);
- }
-}
+ /**
+ * Update chain status and enable/disable various bits of the UI
+ * when the user changes the "unlock move permissions" checkbox
+ */
+ 'onChainClick': function() {
+ if( this.isUnchained() ) {
+ this.enableUnchainedInputs( true );
+ } else {
+ this.setAllSelectors( this.getMaxLevel() );
+ this.enableUnchainedInputs( false );
+ }
+ this.updateCascadeCheckbox();
+ },
+
+ /**
+ * Returns true if the named attribute in all objects in the given array are matching
+ */
+ 'matchAttribute' : function( objects, attrName ) {
+ var value = null;
-/**
- * Get a list of all protection selectors on the page
- *
- * @return Array
- */
-function protectSelectors() {
- 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(/^mwProtect-level-/)) {
- ours[ours.length] = set;
+ // Check levels
+ for ( var i = 0; i < objects.length; i++ ) {
+ var element = objects[i];
+ if ( value == null ) {
+ value = element[attrName];
+ } else {
+ if ( value != element[attrName] ) {
+ return false;
+ }
+ }
}
- }
- return ours;
-}
+ return true;
+ },
-/**
- * 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]);
- }
-}
+ /**
+ * Are all actions protected at the same level, with the same expiry time?
+ *
+ * @return boolean
+ */
+ 'areAllTypesMatching': function() {
+ return this.existingMatch
+ && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
+ && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
+ && this.matchAttribute( this.getExpiryInputs(), 'value' );
+ },
-/**
- * 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;
+ /**
+ * Is protection chaining off?
+ *
+ * @return bool
+ */
+ 'isUnchained': function() {
+ var element = document.getElementById( 'mwProtectUnchained' );
+ return element
+ ? element.checked
+ : true; // No control, so we need to let the user set both levels
+ },
+
+ /**
+ * Find the highest protection level in any selector
+ */
+ 'getMaxLevel': function() {
+ var maxIndex = -1;
+ this.forEachLevelSelector(function(element) {
+ if (element.selectedIndex > maxIndex) {
+ maxIndex = element.selectedIndex;
+ }
+ });
+ return maxIndex;
+ },
+
+ /**
+ * Protect all actions at the specified level
+ *
+ * @param int index Protection level
+ */
+ 'setAllSelectors': function(index) {
+ this.forEachLevelSelector(function(element) {
+ if (element.selectedIndex != index) {
+ element.selectedIndex = index;
+ }
+ });
+ },
+
+ /**
+ * Apply a callback to each protection selector
+ *
+ * @param callable func Callback function
+ */
+ 'forEachLevelSelector': function(func) {
+ var selectors = this.getLevelSelectors();
+ for (var i = 0; i < selectors.length; i++) {
+ func(selectors[i]);
}
- }
- 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 protection selectors on the page
+ *
+ * @return Array
+ */
+ 'getLevelSelectors': function() {
+ var all = document.getElementsByTagName("select");
+ var ours = new Array();
+ for (var i = 0; i < all.length; i++) {
+ var element = all[i];
+ if (element.id.match(/^mwProtect-level-/)) {
+ ours[ours.length] = element;
+ }
+ }
+ return ours;
+ },
-/**
- * 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;
+ /**
+ * Apply a callback to each expiry input
+ *
+ * @param callable func Callback function
+ */
+ 'forEachExpiryInput': function(func) {
+ var inputs = this.getExpiryInputs();
+ for (var i = 0; i < inputs.length; i++) {
+ func(inputs[i]);
}
- }
- return ours;
-}
+ },
-/**
- * Enable/disable protection selectors and expiry inputs
- *
- * @param boolean val Enable?
- */
-function protectEnable(val) {
- // fixme
- var first = true;
- protectForSelectors(function(set) {
- if (first) {
- first = false;
- } else {
- set.disabled = !val;
- set.style.visible = val ? "visible" : "hidden";
+ /**
+ * Get a list of all expiry inputs on the page
+ *
+ * @return Array
+ */
+ 'getExpiryInputs': function() {
+ var all = document.getElementsByTagName("input");
+ var ours = new Array();
+ for (var i = 0; i < all.length; i++) {
+ var element = all[i];
+ if (element.name.match(/^mwProtect-expiry-/)) {
+ ours[ours.length] = element;
+ }
}
- });
- first = true;
- expiryForInputs(function(set) {
- if (first) {
- first = false;
- } else {
- set.disabled = !val;
- set.style.visible = val ? "visible" : "hidden";
+ return ours;
+ },
+
+ /**
+ * Apply a callback to each expiry selector list
+ * @param callable func Callback function
+ */
+ 'forEachExpirySelector': function(func) {
+ var inputs = this.getExpirySelectors();
+ for (var i = 0; i < inputs.length; i++) {
+ func(inputs[i]);
}
- });
- first = true;
- expiryListForInputs(function(set) {
- if (first) {
- first = false;
- } else {
- set.disabled = !val;
- set.style.visible = val ? "visible" : "hidden";
+ },
+
+ /**
+ * Get a list of all expiry selector lists on the page
+ *
+ * @return Array
+ */
+ 'getExpirySelectors': function() {
+ var all = document.getElementsByTagName("select");
+ var ours = new Array();
+ for (var i = 0; i < all.length; i++) {
+ var element = all[i];
+ if (element.id.match(/^mwProtectExpirySelection-/)) {
+ ours[ours.length] = element;
+ }
}
- });
+ return ours;
+ },
+
+ /**
+ * Enable/disable protection selectors and expiry inputs
+ *
+ * @param boolean val Enable?
+ */
+ 'enableUnchainedInputs': function(val) {
+ var first = true;
+ this.forEachLevelSelector(function(element) {
+ if (first) {
+ first = false;
+ } else {
+ element.disabled = !val;
+ }
+ });
+ first = true;
+ this.forEachExpiryInput(function(element) {
+ if (first) {
+ first = false;
+ } else {
+ element.disabled = !val;
+ }
+ });
+ first = true;
+ this.forEachExpirySelector(function(element) {
+ if (first) {
+ first = false;
+ } else {
+ element.disabled = !val;
+ }
+ });
+ }
}