Index: trunk/phase3/skins/common/protect.js |
— | — | @@ -101,6 +101,40 @@ |
102 | 102 | } |
103 | 103 | |
104 | 104 | /** |
| 105 | + * When protection levels are locked together, update the |
| 106 | + * expiries when one changes |
| 107 | + * |
| 108 | + * @param Element source expiry input that changed |
| 109 | + */ |
| 110 | + |
| 111 | +function protectExpiryUpdate(source) { |
| 112 | + if( !protectUnchained() ) { |
| 113 | + var expiry = source.value; |
| 114 | + expiryForInputs(function(set) { |
| 115 | + set.value = expiry; |
| 116 | + }); |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +/** |
| 121 | + * When protection levels are locked together, update the |
| 122 | + * expiry lists when one changes and clear the custom inputs |
| 123 | + * |
| 124 | + * @param Element source expiry selector that changed |
| 125 | + */ |
| 126 | +function protectExpiryListUpdate(source) { |
| 127 | + if( !protectUnchained() ) { |
| 128 | + var expiry = source.value; |
| 129 | + expiryListForInputs(function(set) { |
| 130 | + set.value = expiry; |
| 131 | + }); |
| 132 | + expiryForInputs(function(set) { |
| 133 | + set.value = ''; |
| 134 | + }); |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +/** |
105 | 139 | * Update chain status and enable/disable various bits of the UI |
106 | 140 | * when the user changes the "unlock move permissions" checkbox |
107 | 141 | */ |
— | — | @@ -200,8 +234,65 @@ |
201 | 235 | } |
202 | 236 | |
203 | 237 | /** |
204 | | - * Enable/disable protection selectors |
| 238 | + * Apply a callback to each expiry input |
205 | 239 | * |
| 240 | + * @param callable func Callback function |
| 241 | + */ |
| 242 | +function expiryForInputs(func) { |
| 243 | + var inputs = expiryInputs(); |
| 244 | + for (var i = 0; i < inputs.length; i++) { |
| 245 | + func(inputs[i]); |
| 246 | + } |
| 247 | +} |
| 248 | + |
| 249 | +/** |
| 250 | + * Get a list of all expiry inputs on the page |
| 251 | + * |
| 252 | + * @return Array |
| 253 | + */ |
| 254 | +function expiryInputs() { |
| 255 | + var all = document.getElementsByTagName("input"); |
| 256 | + var ours = new Array(); |
| 257 | + for (var i = 0; i < all.length; i++) { |
| 258 | + var set = all[i]; |
| 259 | + if (set.name.match(/^mwProtect-expiry-/)) { |
| 260 | + ours[ours.length] = set; |
| 261 | + } |
| 262 | + } |
| 263 | + return ours; |
| 264 | +} |
| 265 | + |
| 266 | +/** |
| 267 | + * Apply a callback to each expiry selector list |
| 268 | + * @param callable func Callback function |
| 269 | + */ |
| 270 | +function expiryListForInputs(func) { |
| 271 | + var inputs = expiryListInputs(); |
| 272 | + for (var i = 0; i < inputs.length; i++) { |
| 273 | + func(inputs[i]); |
| 274 | + } |
| 275 | +} |
| 276 | + |
| 277 | +/** |
| 278 | + * Get a list of all expiry selector lists on the page |
| 279 | + * |
| 280 | + * @return Array |
| 281 | + */ |
| 282 | +function expiryListInputs() { |
| 283 | + var all = document.getElementsByTagName("select"); |
| 284 | + var ours = new Array(); |
| 285 | + for (var i = 0; i < all.length; i++) { |
| 286 | + var set = all[i]; |
| 287 | + if (set.id.match(/^mwProtectExpiryList-/)) { |
| 288 | + ours[ours.length] = set; |
| 289 | + } |
| 290 | + } |
| 291 | + return ours; |
| 292 | +} |
| 293 | + |
| 294 | +/** |
| 295 | + * Enable/disable protection selectors and expiry inputs |
| 296 | + * |
206 | 297 | * @param boolean val Enable? |
207 | 298 | */ |
208 | 299 | function protectEnable(val) { |
— | — | @@ -215,4 +306,22 @@ |
216 | 307 | set.style.visible = val ? "visible" : "hidden"; |
217 | 308 | } |
218 | 309 | }); |
| 310 | + first = true; |
| 311 | + expiryForInputs(function(set) { |
| 312 | + if (first) { |
| 313 | + first = false; |
| 314 | + } else { |
| 315 | + set.disabled = !val; |
| 316 | + set.style.visible = val ? "visible" : "hidden"; |
| 317 | + } |
| 318 | + }); |
| 319 | + first = true; |
| 320 | + expiryListForInputs(function(set) { |
| 321 | + if (first) { |
| 322 | + first = false; |
| 323 | + } else { |
| 324 | + set.disabled = !val; |
| 325 | + set.style.visible = val ? "visible" : "hidden"; |
| 326 | + } |
| 327 | + }); |
219 | 328 | } |
Index: trunk/phase3/includes/ProtectionForm.php |
— | — | @@ -25,8 +25,10 @@ |
26 | 26 | class ProtectionForm { |
27 | 27 | var $mRestrictions = array(); |
28 | 28 | var $mReason = ''; |
| 29 | + var $mReasonList = ''; |
29 | 30 | var $mCascade = false; |
30 | | - var $mExpiry = null; |
| 31 | + var $mExpiry =array(); |
| 32 | + var $mExpiryList = array(); |
31 | 33 | var $mPermErrors = array(); |
32 | 34 | var $mApplicableTypes = array(); |
33 | 35 | |
— | — | @@ -44,18 +46,17 @@ |
45 | 47 | // Fixme: this form currently requires individual selections, |
46 | 48 | // but the db allows multiples separated by commas. |
47 | 49 | $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) ); |
| 50 | + |
| 51 | + if ( $this->mTitle->mRestrictionsExpiry[$action] == 'infinity' ) { |
| 52 | + $this->mExpiry[$action] = 'infinite'; |
| 53 | + } else if ( strlen($this->mTitle->mRestrictionsExpiry[$action]) == 0 ) { |
| 54 | + $this->mExpiry[$action] = ''; |
| 55 | + } else { |
| 56 | + // FIXME: this format is not user friendly |
| 57 | + $this->mExpiry[$action] = wfTimestamp( TS_ISO_8601, $this->mTitle->mRestrictionsExpiry[$action] ); |
| 58 | + } |
48 | 59 | } |
49 | | - |
50 | 60 | $this->mCascade = $this->mTitle->areRestrictionsCascading(); |
51 | | - |
52 | | - if ( $this->mTitle->mRestrictionsExpiry == 'infinity' ) { |
53 | | - $this->mExpiry = 'infinite'; |
54 | | - } else if ( strlen($this->mTitle->mRestrictionsExpiry) == 0 ) { |
55 | | - $this->mExpiry = ''; |
56 | | - } else { |
57 | | - // FIXME: this format is not user friendly |
58 | | - $this->mExpiry = wfTimestamp( TS_ISO_8601, $this->mTitle->mRestrictionsExpiry ); |
59 | | - } |
60 | 61 | } |
61 | 62 | |
62 | 63 | // The form will be available in read-only to show levels. |
— | — | @@ -67,14 +68,15 @@ |
68 | 69 | $this->mReason = $wgRequest->getText( 'mwProtect-reason' ); |
69 | 70 | $this->mReasonList = $wgRequest->getText( 'wpProtectReasonList' ); |
70 | 71 | $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade', $this->mCascade ); |
71 | | - // Let dropdown have 'infinite' for unprotected pages |
72 | | - if( !($expiry = $wgRequest->getText( 'mwProtect-expiry' )) && $this->mExpiry != 'infinite' ) { |
73 | | - $expiry = $this->mExpiry; |
74 | | - } |
75 | | - $this->mExpiry = $expiry; |
76 | | - $this->mExpiryList = $wgRequest->getText( 'wpProtectExpiryList', $this->mExpiry ? '' : 'infinite' ); |
77 | | - |
| 72 | + |
78 | 73 | foreach( $this->mApplicableTypes as $action ) { |
| 74 | + // Let dropdown have 'infinite' for unprotected pages |
| 75 | + if( !($expiry[$action] = $wgRequest->getText( "mwProtect-expiry-$action" )) && $this->mExpiry[$action] != 'infinite' ) { |
| 76 | + $expiry[$action] = $this->mExpiry[$action]; |
| 77 | + } |
| 78 | + $this->mExpiry[$action] = $expiry[$action]; |
| 79 | + $this->mExpiryList[$action] = $wgRequest->getText( "wpProtectExpiryList-$action", $this->mExpiry[$action] ? '' : 'infinite' ); |
| 80 | + |
79 | 81 | $val = $wgRequest->getVal( "mwProtect-level-$action" ); |
80 | 82 | if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) { |
81 | 83 | // Prevent users from setting levels that they cannot later unset |
— | — | @@ -175,32 +177,35 @@ |
176 | 178 | } elseif ( $reasonstr == 'other' ) { |
177 | 179 | $reasonstr = $this->mReason; |
178 | 180 | } |
179 | | - # Custom expiry takes precedence |
180 | | - if ( strlen( $this->mExpiry ) == 0 ) { |
181 | | - $this->mExpiry = strlen($this->mExpiryList) ? $this->mExpiryList : 'infinite'; |
182 | | - } |
183 | | - |
184 | | - if ( $this->mExpiry == 'infinite' || $this->mExpiry == 'indefinite' ) { |
185 | | - $expiry = Block::infinity(); |
186 | | - } else { |
187 | | - # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1 |
188 | | - $expiry = strtotime( $this->mExpiry ); |
189 | | - |
190 | | - if ( $expiry < 0 || $expiry === false ) { |
191 | | - $this->show( wfMsg( 'protect_expiry_invalid' ) ); |
192 | | - return false; |
| 181 | + $expiry = array(); |
| 182 | + foreach( $this->mApplicableTypes as $action ) { |
| 183 | + # Custom expiry takes precedence |
| 184 | + if ( strlen( $wgRequest->getText( "mwProtect-expiry-$action" ) ) == 0 ) { |
| 185 | + $this->mExpiry[$action] = strlen($wgRequest->getText( "wpProtectExpiryList-$action")) ? $wgRequest->getText( "wpProtectExpiryList-$action") : 'infinite'; |
| 186 | + } else { |
| 187 | + $this->mExpiry[$action] = $wgRequest->getText( "mwProtect-expiry-$action" ); |
193 | 188 | } |
| 189 | + if ( $this->mExpiry[$action] == 'infinite' || $this->mExpiry[$action] == 'indefinite' ) { |
| 190 | + $expiry[$action] = Block::infinity(); |
| 191 | + } else { |
| 192 | + # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1 |
| 193 | + $expiry[$action] = strtotime( $this->mExpiry[$action] ); |
194 | 194 | |
195 | | - // Fixme: non-qualified absolute times are not in users specified timezone |
196 | | - // and there isn't notice about it in the ui |
197 | | - $expiry = wfTimestamp( TS_MW, $expiry ); |
| 195 | + if ( $expiry[$action] < 0 || $expiry[$action] === false ) { |
| 196 | + $this->show( wfMsg( 'protect_expiry_invalid' ) ); |
| 197 | + return false; |
| 198 | + } |
198 | 199 | |
199 | | - if ( $expiry < wfTimestampNow() ) { |
200 | | - $this->show( wfMsg( 'protect_expiry_old' ) ); |
201 | | - return false; |
| 200 | + // Fixme: non-qualified absolute times are not in users specified timezone |
| 201 | + // and there isn't notice about it in the ui |
| 202 | + $expiry[$action] = wfTimestamp( TS_MW, $expiry[$action] ); |
| 203 | + |
| 204 | + if ( $expiry[$action] < wfTimestampNow() ) { |
| 205 | + $this->show( wfMsg( 'protect_expiry_old' ) ); |
| 206 | + return false; |
| 207 | + } |
202 | 208 | } |
203 | 209 | } |
204 | | - |
205 | 210 | # They shouldn't be able to do this anyway, but just to make sure, ensure that cascading restrictions aren't being applied |
206 | 211 | # to a semi-protected page. |
207 | 212 | global $wgGroupPermissions; |
— | — | @@ -214,7 +219,7 @@ |
215 | 220 | if ($this->mTitle->exists()) { |
216 | 221 | $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $reasonstr, $this->mCascade, $expiry ); |
217 | 222 | } else { |
218 | | - $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $reasonstr, $expiry ); |
| 223 | + $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $reasonstr, $expiry['create'] ); |
219 | 224 | } |
220 | 225 | |
221 | 226 | if( !$ok ) { |
— | — | @@ -238,8 +243,6 @@ |
239 | 244 | function buildForm() { |
240 | 245 | global $wgUser; |
241 | 246 | |
242 | | - $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), 'mwProtectExpiryList' ); |
243 | | - $mProtectother = Xml::label( wfMsg( 'protect-othertime' ), 'expires' ); |
244 | 247 | $mProtectreasonother = Xml::label( wfMsg( 'protectcomment' ), 'wpProtectReasonList' ); |
245 | 248 | $mProtectreason = Xml::label( wfMsg( 'protect-otherreason' ), 'mwProtect-reason' ); |
246 | 249 | |
— | — | @@ -257,10 +260,9 @@ |
258 | 261 | $out .= Xml::openElement( 'fieldset' ) . |
259 | 262 | Xml::element( 'legend', null, wfMsg( 'protect-legend' ) ) . |
260 | 263 | Xml::openElement( 'table', array( 'id' => 'mwProtectSet' ) ) . |
261 | | - Xml::openElement( 'tbody' ) . |
262 | | - "<tr>\n"; |
| 264 | + Xml::openElement( 'tbody' ); |
263 | 265 | |
264 | | - foreach( $this->mRestrictions as $action => $required ) { |
| 266 | + foreach( $this->mRestrictions as $action => $selected ) { |
265 | 267 | /* Not all languages have V_x <-> N_x relation */ |
266 | 268 | $msg = wfMsg( 'restriction-' . $action ); |
267 | 269 | if( wfEmptyMsg( 'restriction-' . $action, $msg ) ) { |
— | — | @@ -269,32 +271,53 @@ |
270 | 272 | $label = Xml::element( 'label', |
271 | 273 | array( 'for' => "mwProtect-level-$action" ), |
272 | 274 | $msg ); |
273 | | - $out .= "<th>$label</th>"; |
274 | | - } |
275 | | - $out .= "</tr> |
276 | | - <tr>\n"; |
277 | | - foreach( $this->mRestrictions as $action => $selected ) { |
278 | | - $out .= "<td>" . |
279 | | - $this->buildSelector( $action, $selected ) . |
280 | | - "</td>"; |
281 | | - } |
282 | | - $out .= "</tr>\n"; |
283 | | - |
284 | | - $scExpiryOptions = wfMsgForContent( 'ipboptions' ); // FIXME: use its own message |
| 275 | + $out .= "<tr><th>$label</th></tr>"; |
| 276 | + $out .= "<tr><td>" . |
| 277 | + $this->buildSelector( $action, $selected ) . |
| 278 | + "</td></tr>"; |
| 279 | + $scExpiryOptions = wfMsgForContent( 'ipboptions' ); // FIXME: use its own message |
285 | 280 | |
286 | | - $showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled); |
287 | | - if( !$showProtectOptions ) |
288 | | - $mProtectother = $mProtectexpiry; |
289 | | - |
290 | | - $expiryFormOptions = Xml::option( wfMsg( 'protect-othertime-op' ), 'wpProtectExpiryList' ); |
291 | | - foreach( explode(',', $scExpiryOptions) as $option ) { |
292 | | - if ( strpos($option, ":") === false ) $option = "$option:$option"; |
293 | | - list($show, $value) = explode(":", $option); |
294 | | - $show = htmlspecialchars($show); |
295 | | - $value = htmlspecialchars($value); |
296 | | - $expiryFormOptions .= Xml::option( $show, $value, $this->mExpiryList === $value ? true : false ) . "\n"; |
| 281 | + $showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled); |
| 282 | + |
| 283 | + $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), "mwProtectExpiryList-$action" ); |
| 284 | + $mProtectother = Xml::label( wfMsg( 'protect-othertime' ), "mwProtect-$action-expires" ); |
| 285 | + $expiryFormOptions = Xml::option( wfMsg( 'protect-othertime-op' ), "wpProtectExpiryList-$action" ); |
| 286 | + foreach( explode(',', $scExpiryOptions) as $option ) { |
| 287 | + if ( strpos($option, ":") === false ) $option = "$option:$option"; |
| 288 | + list($show, $value) = explode(":", $option); |
| 289 | + $show = htmlspecialchars($show); |
| 290 | + $value = htmlspecialchars($value); |
| 291 | + $expiryFormOptions .= Xml::option( $show, $value, $this->mExpiryList[$action] === $value ? true : false ) . "\n"; |
| 292 | + } |
| 293 | + # Add expiry dropdown |
| 294 | + if( $showProtectOptions && !$this->disabled ) { |
| 295 | + $out .= " |
| 296 | + <tr> |
| 297 | + <td class='mw-label'> |
| 298 | + {$mProtectexpiry} |
| 299 | + </td> |
| 300 | + <td class='mw-input'>" . |
| 301 | + Xml::tags( 'select', |
| 302 | + array( |
| 303 | + 'id' => "mwProtectExpiryList-$action", |
| 304 | + 'name' => "wpProtectExpiryList-$action", |
| 305 | + 'onchange' => "protectExpiryListUpdate(this)", |
| 306 | + 'tabindex' => '2' ) + $this->disabledAttrib, |
| 307 | + $expiryFormOptions ) . |
| 308 | + "</td> |
| 309 | + </tr>"; |
| 310 | + } |
| 311 | + # Add custom expiry field |
| 312 | + $attribs = array( 'id' => "mwProtect-$action-expires", 'onkeyup' => 'protectExpiryUpdate(this)' ) + $this->disabledAttrib; |
| 313 | + $out .= "<tr> |
| 314 | + <td class='mw-label'>" . |
| 315 | + $mProtectother . |
| 316 | + '</td> |
| 317 | + <td class="mw-input">' . |
| 318 | + Xml::input( "mwProtect-expiry-$action", 60, $this->mExpiry[$action], $attribs ) . |
| 319 | + '</td> |
| 320 | + </tr>'; |
297 | 321 | } |
298 | | - |
299 | 322 | $reasonDropDown = Xml::listDropDown( 'wpProtectReasonList', |
300 | 323 | wfMsgForContent( 'protect-dropdown' ), |
301 | 324 | wfMsgForContent( 'protect-otherreason-op' ), '', 'mwProtect-reason', 4 ); |
— | — | @@ -314,34 +337,6 @@ |
315 | 338 | "</td> |
316 | 339 | </tr>\n"; |
317 | 340 | } |
318 | | - # Add expiry dropdown |
319 | | - if( $showProtectOptions && !$this->disabled ) { |
320 | | - $out .= " |
321 | | - <tr> |
322 | | - <td class='mw-label'> |
323 | | - {$mProtectexpiry} |
324 | | - </td> |
325 | | - <td class='mw-input'>" . |
326 | | - Xml::tags( 'select', |
327 | | - array( |
328 | | - 'id' => 'mwProtectExpiryList', |
329 | | - 'name' => 'wpProtectExpiryList', |
330 | | - 'onchange' => "document.getElementById('expires').value='';", |
331 | | - 'tabindex' => '2' ) + $this->disabledAttrib, |
332 | | - $expiryFormOptions ) . |
333 | | - "</td> |
334 | | - </tr>"; |
335 | | - } |
336 | | - # Add custom expiry field |
337 | | - $attribs = array( 'id' => 'expires' ) + $this->disabledAttrib; |
338 | | - $out .= "<tr> |
339 | | - <td class='mw-label'>" . |
340 | | - $mProtectother . |
341 | | - '</td> |
342 | | - <td class="mw-input">' . |
343 | | - Xml::input( 'mwProtect-expiry', 60, $this->mExpiry, $attribs ) . |
344 | | - '</td> |
345 | | - </tr>'; |
346 | 341 | # Add manual and custom reason field/selects |
347 | 342 | if( !$this->disabled ) { |
348 | 343 | $out .= " |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -1798,7 +1798,7 @@ |
1799 | 1799 | * @param string $reason |
1800 | 1800 | * @return bool true on success |
1801 | 1801 | */ |
1802 | | - function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) { |
| 1802 | + function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = array() ) { |
1803 | 1803 | global $wgUser, $wgRestrictionTypes, $wgContLang; |
1804 | 1804 | |
1805 | 1805 | $id = $this->mTitle->getArticleID(); |
— | — | @@ -1816,15 +1816,17 @@ |
1817 | 1817 | # FIXME: Same limitations as described in ProtectionForm.php (line 37); |
1818 | 1818 | # we expect a single selection, but the schema allows otherwise. |
1819 | 1819 | $current = array(); |
1820 | | - foreach( $wgRestrictionTypes as $action ) |
| 1820 | + $updated = Article::flattenRestrictions( $limit ); |
| 1821 | + $changed = false; |
| 1822 | + foreach( $wgRestrictionTypes as $action ) { |
1821 | 1823 | $current[$action] = implode( '', $this->mTitle->getRestrictions( $action ) ); |
| 1824 | + $changed = ($changed || ($this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action]) ); |
| 1825 | + } |
1822 | 1826 | |
1823 | 1827 | $current = Article::flattenRestrictions( $current ); |
1824 | | - $updated = Article::flattenRestrictions( $limit ); |
1825 | 1828 | |
1826 | | - $changed = ( $current != $updated ); |
| 1829 | + $changed = ($changed || ( $current != $updated ) ); |
1827 | 1830 | $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade); |
1828 | | - $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry); |
1829 | 1831 | $protect = ( $updated != '' ); |
1830 | 1832 | |
1831 | 1833 | # If nothing's changed, do nothing |
— | — | @@ -1832,14 +1834,6 @@ |
1833 | 1835 | if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) { |
1834 | 1836 | |
1835 | 1837 | $dbw = wfGetDB( DB_MASTER ); |
1836 | | - |
1837 | | - $encodedExpiry = Block::encodeExpiry($expiry, $dbw ); |
1838 | | - |
1839 | | - $expiry_description = ''; |
1840 | | - if( $encodedExpiry != 'infinity' ) { |
1841 | | - $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', |
1842 | | - $wgContLang->timeanddate( $expiry, false, false ) ).')'; |
1843 | | - } |
1844 | 1838 | |
1845 | 1839 | # Prepare a null revision to be added to the history |
1846 | 1840 | $modified = $current != '' && $protect; |
— | — | @@ -1859,7 +1853,6 @@ |
1860 | 1854 | break; |
1861 | 1855 | } |
1862 | 1856 | } |
1863 | | - |
1864 | 1857 | $cascade_description = ''; |
1865 | 1858 | if( $cascade ) { |
1866 | 1859 | $cascade_description = ' ['.wfMsgForContent('protect-summary-cascade').']'; |
— | — | @@ -1869,10 +1862,23 @@ |
1870 | 1863 | $comment .= ": $reason"; |
1871 | 1864 | |
1872 | 1865 | $editComment = $comment; |
1873 | | - if( $protect ) |
1874 | | - $editComment .= " [$updated]"; |
1875 | | - if( $expiry_description && $protect ) |
1876 | | - $editComment .= "$expiry_description"; |
| 1866 | + $encodedExpiry = array(); |
| 1867 | + $protect_description = ''; |
| 1868 | + foreach( $limit as $action => $restrictions ) { |
| 1869 | + $encodedExpiry[$action] = Block::encodeExpiry($expiry[$action], $dbw ); |
| 1870 | + if ($restrictions != '') { |
| 1871 | + $protect_description .= "[$action=$restrictions] ("; |
| 1872 | + if( $encodedExpiry[$action] != 'infinity' ) { |
| 1873 | + $protect_description .= wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry[$action], false, false ) ); |
| 1874 | + } else { |
| 1875 | + $protect_description .= wfMsgForContent( 'protect-expiry-indefinite' ); |
| 1876 | + } |
| 1877 | + $protect_description .= ') '; |
| 1878 | + } |
| 1879 | + } |
| 1880 | + |
| 1881 | + if( $protect_description && $protect ) |
| 1882 | + $editComment .= "($protect_description)"; |
1877 | 1883 | if( $cascade ) |
1878 | 1884 | $editComment .= "$cascade_description"; |
1879 | 1885 | # Update restrictions table |
— | — | @@ -1880,8 +1886,8 @@ |
1881 | 1887 | if ($restrictions != '' ) { |
1882 | 1888 | $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')), |
1883 | 1889 | array( 'pr_page' => $id, 'pr_type' => $action |
1884 | | - , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0 |
1885 | | - , 'pr_expiry' => $encodedExpiry ), __METHOD__ ); |
| 1890 | + , 'pr_level' => $restrictions, 'pr_cascade' => $cascade && $action == 'edit' ? 1 : 0 |
| 1891 | + , 'pr_expiry' => $encodedExpiry[$action] ), __METHOD__ ); |
1886 | 1892 | } else { |
1887 | 1893 | $dbw->delete( 'page_restrictions', array( 'pr_page' => $id, |
1888 | 1894 | 'pr_type' => $action ), __METHOD__ ); |
— | — | @@ -1910,7 +1916,7 @@ |
1911 | 1917 | # Update the protection log |
1912 | 1918 | $log = new LogPage( 'protect' ); |
1913 | 1919 | if( $protect ) { |
1914 | | - $params = array($updated,$encodedExpiry,$cascade ? 'cascade' : ''); |
| 1920 | + $params = array($protect_description,$cascade ? 'cascade' : ''); |
1915 | 1921 | $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason), $params ); |
1916 | 1922 | } else { |
1917 | 1923 | $log->addEntry( 'unprotect', $this->mTitle, $reason ); |
Index: trunk/phase3/includes/Title.php |
— | — | @@ -56,7 +56,7 @@ |
57 | 57 | var $mRestrictions = array(); ///< Array of groups allowed to edit this article |
58 | 58 | var $mOldRestrictions = false; |
59 | 59 | var $mCascadeRestriction; ///< Cascade restrictions on this page to included templates and images? |
60 | | - var $mRestrictionsExpiry; ///< When do the restrictions on this page expire? |
| 60 | + var $mRestrictionsExpiry = array(); ///< When do the restrictions on this page expire? |
61 | 61 | var $mHasCascadingRestrictions; ///< Are cascading restrictions in effect on this page? |
62 | 62 | var $mCascadeSources; ///< Where are the cascading restrictions coming from on this page? |
63 | 63 | var $mRestrictionsLoaded = false; ///< Boolean for initialisation on demand |
— | — | @@ -1368,7 +1368,7 @@ |
1369 | 1369 | global $wgUser,$wgContLang; |
1370 | 1370 | |
1371 | 1371 | if ($create_perm == implode(',',$this->getRestrictions('create')) |
1372 | | - && $expiry == $this->mRestrictionsExpiry) { |
| 1372 | + && $expiry == $this->mRestrictionsExpiry['create']) { |
1373 | 1373 | // No change |
1374 | 1374 | return true; |
1375 | 1375 | } |
— | — | @@ -1383,7 +1383,10 @@ |
1384 | 1384 | if ( $encodedExpiry != 'infinity' ) { |
1385 | 1385 | $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')'; |
1386 | 1386 | } |
1387 | | - |
| 1387 | + else { |
| 1388 | + $expiry_description .= ' (' . wfMsgForContent( 'protect-expiry-indefinite' ).')'; |
| 1389 | + } |
| 1390 | + |
1388 | 1391 | # Update protection table |
1389 | 1392 | if ($create_perm != '' ) { |
1390 | 1393 | $dbw->replace( 'protected_titles', array(array('pt_namespace', 'pt_title')), |
— | — | @@ -1740,7 +1743,6 @@ |
1741 | 1744 | } else { |
1742 | 1745 | $this->mHasCascadingRestrictions = $sources; |
1743 | 1746 | } |
1744 | | - |
1745 | 1747 | return array( $sources, $pagerestrictions ); |
1746 | 1748 | } |
1747 | 1749 | |
— | — | @@ -1762,10 +1764,10 @@ |
1763 | 1765 | |
1764 | 1766 | foreach( $wgRestrictionTypes as $type ){ |
1765 | 1767 | $this->mRestrictions[$type] = array(); |
| 1768 | + $this->mRestrictionsExpiry[$type] = Block::decodeExpiry(''); |
1766 | 1769 | } |
1767 | 1770 | |
1768 | 1771 | $this->mCascadeRestriction = false; |
1769 | | - $this->mRestrictionsExpiry = Block::decodeExpiry(''); |
1770 | 1772 | |
1771 | 1773 | # Backwards-compatibility: also load the restrictions from the page record (old format). |
1772 | 1774 | |
— | — | @@ -1809,7 +1811,7 @@ |
1810 | 1812 | |
1811 | 1813 | // Only apply the restrictions if they haven't expired! |
1812 | 1814 | if ( !$expiry || $expiry > $now ) { |
1813 | | - $this->mRestrictionsExpiry = $expiry; |
| 1815 | + $this->mRestrictionsExpiry[$row->pr_type] = $expiry; |
1814 | 1816 | $this->mRestrictions[$row->pr_type] = explode( ',', trim( $row->pr_level ) ); |
1815 | 1817 | |
1816 | 1818 | $this->mCascadeRestriction |= $row->pr_cascade; |
— | — | @@ -1850,13 +1852,13 @@ |
1851 | 1853 | |
1852 | 1854 | if (!$expiry || $expiry > $now) { |
1853 | 1855 | // Apply the restrictions |
1854 | | - $this->mRestrictionsExpiry = $expiry; |
| 1856 | + $this->mRestrictionsExpiry['create'] = $expiry; |
1855 | 1857 | $this->mRestrictions['create'] = explode(',', trim($pt_create_perm) ); |
1856 | 1858 | } else { // Get rid of the old restrictions |
1857 | 1859 | Title::purgeExpiredRestrictions(); |
1858 | 1860 | } |
1859 | 1861 | } else { |
1860 | | - $this->mRestrictionsExpiry = Block::decodeExpiry(''); |
| 1862 | + $this->mRestrictionsExpiry['create'] = Block::decodeExpiry(''); |
1861 | 1863 | } |
1862 | 1864 | $this->mRestrictionsLoaded = true; |
1863 | 1865 | } |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -1383,7 +1383,7 @@ |
1384 | 1384 | * to ensure that client-side caches don't keep obsolete copies of global |
1385 | 1385 | * styles. |
1386 | 1386 | */ |
1387 | | -$wgStyleVersion = '176'; |
| 1387 | +$wgStyleVersion = '177'; |
1388 | 1388 | |
1389 | 1389 | |
1390 | 1390 | # Server-side caching: |
Index: trunk/phase3/includes/specials/SpecialMovepage.php |
— | — | @@ -292,6 +292,10 @@ |
293 | 293 | |
294 | 294 | $error = $ot->moveTo( $nt, true, $this->reason ); |
295 | 295 | if ( $error !== true ) { |
| 296 | + if (isset($error[0][0]) && $error[0][0] = 'cascadeprotected') { |
| 297 | + $wgOut->showPermissionsErrorPage($error, 'move'); |
| 298 | + return; |
| 299 | + } |
296 | 300 | # FIXME: showForm() should handle multiple errors |
297 | 301 | call_user_func_array(array($this, 'showForm'), $error[0]); |
298 | 302 | return; |
Index: trunk/phase3/includes/LogPage.php |
— | — | @@ -203,13 +203,9 @@ |
204 | 204 | } |
205 | 205 | $params[2] = isset( $params[2] ) ? |
206 | 206 | self::formatBlockFlags( $params[2], is_null( $skin ) ) : ''; |
207 | | - } else if ( $type == 'protect' && count($params) == 4 ) { |
208 | | - $details .= " [{$params[1]}]"; // the restrictions |
209 | | - if( $params[2] != 'infinity' ) { |
210 | | - $details .= ' (' . wfMsgForContent( 'protect-expiring', |
211 | | - $wgContLang->timeanddate( $params[2], false, false ) ).')'; |
212 | | - } |
213 | | - if( $params[3] ) { |
| 207 | + } else if ( $type == 'protect' && count($params) == 3 ) { |
| 208 | + $details .= " {$params[1]}"; // restrictions and expiries |
| 209 | + if( $params[2] ) { |
214 | 210 | $details .= ' ['.wfMsg('protect-summary-cascade').']'; |
215 | 211 | } |
216 | 212 | } |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -2315,6 +2315,7 @@ |
2316 | 2316 | 'protect-level-sysop' => 'Sysops only', |
2317 | 2317 | 'protect-summary-cascade' => 'cascading', |
2318 | 2318 | 'protect-expiring' => 'expires $1 (UTC)', |
| 2319 | +'protect-expiry-indefinite' => 'indefinite', |
2319 | 2320 | 'protect-cascade' => 'Protect pages included in this page (cascading protection)', |
2320 | 2321 | 'protect-cantedit' => 'You cannot change the protection levels of this page, because you do not have permission to edit it.', |
2321 | 2322 | 'protect-othertime' => 'Other time:', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -124,6 +124,8 @@ |
125 | 125 | * (bug 15551) Deletion log excerpt is now shown whenever a user vists a |
126 | 126 | deleted page, even if they are unable to edit it. |
127 | 127 | * Added Wantedfiles special pages, allowing users to find image links with no image. |
| 128 | +* (bug 12650) It is now possible to set different expiration times for different |
| 129 | + restriction types on the protection form. |
128 | 130 | |
129 | 131 | === Bug fixes in 1.14 === |
130 | 132 | |