Index: trunk/phase3/maintenance/archives/patch-user_registration.sql |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +-- |
| 3 | +-- New user field for tracking registration time |
| 4 | +-- 2005-12-21 |
| 5 | +-- |
| 6 | + |
| 7 | +ALTER TABLE /*$wgDBprefix*/user |
| 8 | + -- Timestamp of account registration. |
| 9 | + -- Accounts predating this schema addition may contain NULL. |
| 10 | + ADD user_registration CHAR(14) BINARY; |
Property changes on: trunk/phase3/maintenance/archives/patch-user_registration.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 11 | + native |
Added: svn:keywords |
2 | 12 | + Author Date Id Revision |
Index: trunk/phase3/maintenance/mysql5/tables.sql |
— | — | @@ -119,6 +119,10 @@ |
120 | 120 | -- Expiration date for the user_email_token |
121 | 121 | user_email_token_expires CHAR(14) BINARY, |
122 | 122 | |
| 123 | + -- Timestamp of account registration. |
| 124 | + -- Accounts predating this schema addition may contain NULL. |
| 125 | + user_registration CHAR(14) BINARY, |
| 126 | + |
123 | 127 | PRIMARY KEY user_id (user_id), |
124 | 128 | UNIQUE INDEX user_name (user_name), |
125 | 129 | INDEX (user_email_token) |
Index: trunk/phase3/maintenance/updaters.inc |
— | — | @@ -39,6 +39,7 @@ |
40 | 40 | array( 'user', 'user_real_name', 'patch-user-realname.sql' ), |
41 | 41 | array( 'user', 'user_token', 'patch-user_token.sql' ), |
42 | 42 | array( 'user', 'user_email_token', 'patch-user_email_token.sql' ), |
| 43 | + array( 'user', 'user_registration','patch-user_registration.sql' ), |
43 | 44 | array( 'logging', 'log_params', 'patch-log_params.sql' ), |
44 | 45 | array( 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ), |
45 | 46 | array( 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ), |
Index: trunk/phase3/maintenance/tables.sql |
— | — | @@ -105,6 +105,10 @@ |
106 | 106 | |
107 | 107 | -- Expiration date for the user_email_token |
108 | 108 | user_email_token_expires CHAR(14) BINARY, |
| 109 | + |
| 110 | + -- Timestamp of account registration. |
| 111 | + -- Accounts predating this schema addition may contain NULL. |
| 112 | + user_registration CHAR(14) BINARY, |
109 | 113 | |
110 | 114 | PRIMARY KEY user_id (user_id), |
111 | 115 | UNIQUE INDEX user_name (user_name), |
Index: trunk/phase3/skins/common/protect.js |
— | — | @@ -0,0 +1,126 @@ |
| 2 | +function protectInitialize(tableId, labelText) { |
| 3 | + if (document.createTextNode) { |
| 4 | + var box = document.getElementById(tableId); |
| 5 | + if (!box) |
| 6 | + return false; |
| 7 | + |
| 8 | + var tbody = box.getElementsByTagName('tbody')[0]; |
| 9 | + var row = document.createElement('tr'); |
| 10 | + tbody.appendChild(row); |
| 11 | + |
| 12 | + row.appendChild(document.createElement('td')); |
| 13 | + var col2 = document.createElement('td'); |
| 14 | + row.appendChild(col2); |
| 15 | + |
| 16 | + var check = document.createElement('input'); |
| 17 | + check.id = "mwProtectUnchained"; |
| 18 | + check.type = "checkbox"; |
| 19 | + check.onclick = protectChainUpdate; |
| 20 | + col2.appendChild(check); |
| 21 | + |
| 22 | + var label = document.createElement('label'); |
| 23 | + label.setAttribute("for", "mwProtectUnchained"); |
| 24 | + label.appendChild(document.createTextNode(labelText)); |
| 25 | + col2.appendChild(label); |
| 26 | + |
| 27 | + if (protectAllMatch()) { |
| 28 | + check.checked = false; |
| 29 | + protectEnable(false); |
| 30 | + } else { |
| 31 | + check.checked = true; |
| 32 | + protectEnable(true); |
| 33 | + } |
| 34 | + |
| 35 | + return true; |
| 36 | + } |
| 37 | + return false; |
| 38 | +} |
| 39 | + |
| 40 | +function protectLevelsUpdate(source) { |
| 41 | + if (!protectUnchained()) { |
| 42 | + protectUpdateAll(source.selectedIndex); |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +function protectChainUpdate() { |
| 47 | + if (protectUnchained()) { |
| 48 | + protectEnable(true); |
| 49 | + } else { |
| 50 | + protectChain(); |
| 51 | + protectEnable(false); |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | + |
| 56 | +function protectAllMatch() { |
| 57 | + var values = new Array(); |
| 58 | + protectForSelectors(function(set) { |
| 59 | + values[values.length] = set.selectedIndex; |
| 60 | + }); |
| 61 | + for (var i = 1; i < values.length; i++) { |
| 62 | + if (values[i] != values[0]) { |
| 63 | + return false; |
| 64 | + } |
| 65 | + } |
| 66 | + return true; |
| 67 | +} |
| 68 | + |
| 69 | +function protectUnchained() { |
| 70 | + var unchain = document.getElementById("mwProtectUnchained"); |
| 71 | + if (!unchain) { |
| 72 | + alert("This shouldn't happen"); |
| 73 | + return false; |
| 74 | + } |
| 75 | + return unchain.checked; |
| 76 | +} |
| 77 | + |
| 78 | +function protectChain() { |
| 79 | + / Find the highest-protected action and bump them all to this level |
| 80 | + var maxIndex = -1; |
| 81 | + protectForSelectors(function(set) { |
| 82 | + if (set.selectedIndex > maxIndex) { |
| 83 | + maxIndex = set.selectedIndex; |
| 84 | + } |
| 85 | + }); |
| 86 | + protectUpdateAll(maxIndex); |
| 87 | +} |
| 88 | + |
| 89 | +function protectUpdateAll(index) { |
| 90 | + protectForSelectors(function(set) { |
| 91 | + if (set.selectedIndex != index) { |
| 92 | + set.selectedIndex = index; |
| 93 | + } |
| 94 | + }); |
| 95 | +} |
| 96 | + |
| 97 | +function protectForSelectors(func) { |
| 98 | + var selectors = protectSelectors(); |
| 99 | + for (var i = 0; i < selectors.length; i++) { |
| 100 | + func(selectors[i]); |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +function protectSelectors() { |
| 105 | + var all = document.getElementsByTagName("select"); |
| 106 | + var ours = new Array(); |
| 107 | + for (var i = 0; i < all.length; i++) { |
| 108 | + var set = all[i]; |
| 109 | + if (set.id.match(/^mwProtect-level-/)) { |
| 110 | + ours[ours.length] = set; |
| 111 | + } |
| 112 | + } |
| 113 | + return ours; |
| 114 | +} |
| 115 | + |
| 116 | +function protectEnable(val) { |
| 117 | + / fixme |
| 118 | + var first = true; |
| 119 | + protectForSelectors(function(set) { |
| 120 | + if (first) { |
| 121 | + first = false; |
| 122 | + } else { |
| 123 | + set.disabled = !val; |
| 124 | + set.style.visible = val ? "visible" : "hidden"; |
| 125 | + } |
| 126 | + }); |
| 127 | +} |
Property changes on: trunk/phase3/skins/common/protect.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 128 | + native |
Added: svn:keywords |
2 | 129 | + Author Date Id Revision |
Index: trunk/phase3/includes/ProtectionForm.php |
— | — | @@ -0,0 +1,244 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Copyright (C) 2005 Brion Vibber <[email protected]> |
| 5 | + * http://www.mediawiki.org/ |
| 6 | + * |
| 7 | + * This program is free software; you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU General Public License as published by |
| 9 | + * the Free Software Foundation; either version 2 of the License, or |
| 10 | + * (at your option) any later version. |
| 11 | + * |
| 12 | + * This program is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU General Public License along |
| 18 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 19 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 20 | + * http://www.gnu.org/copyleft/gpl.html |
| 21 | + * |
| 22 | + * @package MediaWiki |
| 23 | + * @subpackage SpecialPage |
| 24 | + */ |
| 25 | + |
| 26 | +class ProtectionForm { |
| 27 | + var $mRestrictions = array(); |
| 28 | + var $mReason = ''; |
| 29 | + |
| 30 | + function ProtectionForm( &$article ) { |
| 31 | + global $wgRequest, $wgUser; |
| 32 | + global $wgRestrictionTypes, $wgRestrictionLevels; |
| 33 | + $this->mArticle =& $article; |
| 34 | + $this->mTitle =& $article->mTitle; |
| 35 | + |
| 36 | + if( $this->mTitle ) { |
| 37 | + foreach( $wgRestrictionTypes as $action ) { |
| 38 | + / Fixme: this form currently requires individual selections, |
| 39 | + / but the db allows multiples separated by commas. |
| 40 | + $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) ); |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + / The form will be available in read-only to show levels. |
| 45 | + $this->disabled = !$wgUser->isAllowed( 'protect' ) || wfReadOnly(); |
| 46 | + $this->disabledAttrib = $this->disabled |
| 47 | + ? array( 'disabled' => 'disabled' ) |
| 48 | + : array(); |
| 49 | + |
| 50 | + if( $wgRequest->wasPosted() ) { |
| 51 | + $this->mReason = $wgRequest->getText( 'mwProtect-reason' ); |
| 52 | + foreach( $wgRestrictionTypes as $action ) { |
| 53 | + $val = $wgRequest->getVal( "mwProtect-level-$action" ); |
| 54 | + if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) { |
| 55 | + $this->mRestrictions[$action] = $val; |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + function show() { |
| 62 | + global $wgOut; |
| 63 | + |
| 64 | + $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
| 65 | + |
| 66 | + if( is_null( $this->mTitle ) || |
| 67 | + !$this->mTitle->exists() || |
| 68 | + $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { |
| 69 | + $wgOut->fatalError( wfMsg( 'badarticleerror' ) ); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + if( $this->save() ) { |
| 74 | + $wgOut->redirect( $this->mTitle->getFullUrl() ); |
| 75 | + return; |
| 76 | + } |
| 77 | + |
| 78 | + $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); |
| 79 | + $wgOut->setSubtitle( wfMsg( 'protectsub', $this->mTitle->getPrefixedText() ) ); |
| 80 | + |
| 81 | + $wgOut->addWikiText( |
| 82 | + wfMsg( $this->disabled ? "protect-viewtext" : "protect-text", |
| 83 | + $this->mTitle->getPrefixedText() ) ); |
| 84 | + |
| 85 | + $wgOut->addHTML( $this->buildForm() ); |
| 86 | + |
| 87 | + $this->showLogExtract( $wgOut ); |
| 88 | + } |
| 89 | + |
| 90 | + function save() { |
| 91 | + global $wgRequest, $wgUser; |
| 92 | + if( !$wgRequest->wasPosted() ) { |
| 93 | + return false; |
| 94 | + } |
| 95 | + |
| 96 | + if( $this->disabled ) { |
| 97 | + return false; |
| 98 | + } |
| 99 | + |
| 100 | + $token = $wgRequest->getVal( 'wpEditToken' ); |
| 101 | + if( !$wgUser->matchEditToken( $token ) ) { |
| 102 | + $wgOut->fatalError( wfMsg( 'sessionfailure' ) ); |
| 103 | + return false; |
| 104 | + } |
| 105 | + |
| 106 | + $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $this->mReason ); |
| 107 | + if( !$ok ) { |
| 108 | + $wgOut->fatalError( "Unknown error at restriction save time." ); |
| 109 | + } |
| 110 | + return $ok; |
| 111 | + } |
| 112 | + |
| 113 | + function buildForm() { |
| 114 | + global $wgUser; |
| 115 | + |
| 116 | + $out = ''; |
| 117 | + if( !$this->disabled ) { |
| 118 | + $out .= $this->buildScript(); |
| 119 | + / The submission needs to reenable the move permission selector |
| 120 | + / if it's in locked mode, or some browsers won't submit the data. |
| 121 | + $out .= wfOpenElement( 'form', array( |
| 122 | + 'action' => $this->mTitle->getLocalUrl( 'action=protect' ), |
| 123 | + 'method' => 'post', |
| 124 | + 'onsubmit' => 'protectEnable(true)' ) ); |
| 125 | + |
| 126 | + $out .= wfElement( 'input', array( |
| 127 | + 'type' => 'hidden', |
| 128 | + 'name' => 'wpEditToken', |
| 129 | + 'value' => $wgUser->editToken() ) ); |
| 130 | + } |
| 131 | + |
| 132 | + $out .= "<table id='mwProtectSet'>"; |
| 133 | + $out .= "<tbody>"; |
| 134 | + $out .= "<tr>\n"; |
| 135 | + foreach( $this->mRestrictions as $action => $required ) { |
| 136 | + $out .= "<th>" . wfMsgHtml( $action ) . "</th>\n"; |
| 137 | + } |
| 138 | + $out .= "</tr>\n"; |
| 139 | + $out .= "<tr>\n"; |
| 140 | + foreach( $this->mRestrictions as $action => $selected ) { |
| 141 | + $out .= "<td>\n"; |
| 142 | + $out .= $this->buildSelector( $action, $selected ); |
| 143 | + $out .= "</td>\n"; |
| 144 | + } |
| 145 | + $out .= "</tr>\n"; |
| 146 | + |
| 147 | + / JavaScript will add another row with a value-chaining checkbox |
| 148 | + |
| 149 | + $out .= "</tbody>\n"; |
| 150 | + $out .= "</table>\n"; |
| 151 | + |
| 152 | + if( !$this->disabled ) { |
| 153 | + $out .= "<table>\n"; |
| 154 | + $out .= "<tbody>\n"; |
| 155 | + $out .= "<tr><td>" . $this->buildReasonInput() . "</td></tr>\n"; |
| 156 | + $out .= "<tr><td></td><td>" . $this->buildSubmit() . "</td></tr>\n"; |
| 157 | + $out .= "</tbody>\n"; |
| 158 | + $out .= "</table>\n"; |
| 159 | + $out .= "</form>\n"; |
| 160 | + $out .= $this->buildCleanupScript(); |
| 161 | + } |
| 162 | + |
| 163 | + return $out; |
| 164 | + } |
| 165 | + |
| 166 | + function buildSelector( $action, $selected ) { |
| 167 | + global $wgRestrictionLevels; |
| 168 | + $id = 'mwProtect-level-' . $action; |
| 169 | + $attribs = array( |
| 170 | + 'id' => $id, |
| 171 | + 'name' => $id, |
| 172 | + 'size' => count( $wgRestrictionLevels ), |
| 173 | + 'onchange' => 'protectLevelsUpdate(this)', |
| 174 | + ) + $this->disabledAttrib; |
| 175 | + |
| 176 | + $out = wfOpenElement( 'select', $attribs ); |
| 177 | + foreach( $wgRestrictionLevels as $key ) { |
| 178 | + $out .= $this->buildOption( $key, $selected ); |
| 179 | + } |
| 180 | + $out .= "</select>\n"; |
| 181 | + return $out; |
| 182 | + } |
| 183 | + |
| 184 | + function buildOption( $key, $selected ) { |
| 185 | + $text = ( $key == '' ) |
| 186 | + ? wfMsg( 'protect-default' ) |
| 187 | + : wfMsg( "protect-level-$key" ); |
| 188 | + $selectedAttrib = ($selected == $key) |
| 189 | + ? array( 'selected' => 'selected' ) |
| 190 | + : array(); |
| 191 | + return wfElement( 'option', |
| 192 | + array( 'value' => $key ) + $selectedAttrib, |
| 193 | + $text ); |
| 194 | + } |
| 195 | + |
| 196 | + function buildReasonInput() { |
| 197 | + $id = 'mwProtect-reason'; |
| 198 | + return wfElement( 'label', array( |
| 199 | + 'id' => "$id-label", |
| 200 | + 'for' => $id ), |
| 201 | + wfMsg( 'protectcomment' ) ) . |
| 202 | + '</td><td>' . |
| 203 | + wfElement( 'input', array( |
| 204 | + 'size' => 60, |
| 205 | + 'name' => $id, |
| 206 | + 'id' => $id ) ); |
| 207 | + } |
| 208 | + |
| 209 | + function buildSubmit() { |
| 210 | + return wfElement( 'input', array( |
| 211 | + 'type' => 'submit', |
| 212 | + 'value' => wfMsg( 'confirm' ) ) ); |
| 213 | + } |
| 214 | + |
| 215 | + function buildScript() { |
| 216 | + global $wgStylePath; |
| 217 | + return '<script type="text/javascript" src="' . |
| 218 | + htmlspecialchars( $wgStylePath . "/common/protect.js" ) . |
| 219 | + '"></script>'; |
| 220 | + } |
| 221 | + |
| 222 | + function buildCleanupScript() { |
| 223 | + return '<script type="text/javascript">protectInitialize("mwProtectSet","' . |
| 224 | + wfEscapeJsString( wfMsg( 'protect-unchain' ) ) . '")</script>'; |
| 225 | + } |
| 226 | + |
| 227 | + /** |
| 228 | + * @param OutputPage $out |
| 229 | + * @access private |
| 230 | + */ |
| 231 | + function showLogExtract( &$out ) { |
| 232 | + # Show relevant lines from the deletion log: |
| 233 | + $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'protect' ) ) . "</h2>\n" ); |
| 234 | + require_once( 'SpecialLog.php' ); |
| 235 | + $logViewer = new LogViewer( |
| 236 | + new LogReader( |
| 237 | + new FauxRequest( |
| 238 | + array( 'page' => $this->mTitle->getPrefixedText(), |
| 239 | + 'type' => 'protect' ) ) ) ); |
| 240 | + $logViewer->showList( $out ); |
| 241 | + } |
| 242 | +} |
| 243 | + |
| 244 | + |
| 245 | +?> |
\ No newline at end of file |
Property changes on: trunk/phase3/includes/ProtectionForm.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 246 | + native |
Added: svn:keywords |
2 | 247 | + Author Date Id Revision |
Index: trunk/phase3/includes/User.php |
— | — | @@ -14,7 +14,7 @@ |
15 | 15 | define( 'USER_TOKEN_LENGTH', 32 ); |
16 | 16 | |
17 | 17 | # Serialized record version |
18 | | -define( 'MW_USER_VERSION', 2 ); |
| 18 | +define( 'MW_USER_VERSION', 3 ); |
19 | 19 | |
20 | 20 | /** |
21 | 21 | * |
— | — | @@ -36,6 +36,7 @@ |
37 | 37 | var $mHash; |
38 | 38 | var $mGroups; |
39 | 39 | var $mVersion; / serialized version |
| 40 | + var $mRegistration; |
40 | 41 | |
41 | 42 | /** Construct using User:loadDefaults() */ |
42 | 43 | function User() { |
— | — | @@ -107,7 +108,7 @@ |
108 | 109 | return array( 'mId', 'mName', 'mPassword', 'mEmail', 'mNewtalk', |
109 | 110 | 'mEmailAuthenticated', 'mRights', 'mOptions', 'mDataLoaded', |
110 | 111 | 'mNewpassword', 'mBlockedby', 'mBlockreason', 'mTouched', |
111 | | - 'mToken', 'mRealName', 'mHash', 'mGroups' ); |
| 112 | + 'mToken', 'mRealName', 'mHash', 'mGroups', 'mRegistration' ); |
112 | 113 | } |
113 | 114 | |
114 | 115 | /** |
— | — | @@ -321,6 +322,8 @@ |
322 | 323 | $this->mTouched = '0'; # Allow any pages to be cached |
323 | 324 | } |
324 | 325 | |
| 326 | + $this->mRegistration = wfTimestamp( TS_MW ); |
| 327 | + |
325 | 328 | wfProfileOut( $fname ); |
326 | 329 | } |
327 | 330 | |
— | — | @@ -651,7 +654,7 @@ |
652 | 655 | } else { |
653 | 656 | wfDebug( "User::loadFromSession() got from cache!\n" ); |
654 | 657 | } |
655 | | - |
| 658 | + |
656 | 659 | if ( isset( $_SESSION['wsToken'] ) ) { |
657 | 660 | $passwordCorrect = $_SESSION['wsToken'] == $user->mToken; |
658 | 661 | } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) { |
— | — | @@ -699,7 +702,7 @@ |
700 | 703 | $dbr =& wfGetDB( DB_SLAVE ); |
701 | 704 | $s = $dbr->selectRow( 'user', array( 'user_name','user_password','user_newpassword','user_email', |
702 | 705 | 'user_email_authenticated', |
703 | | - 'user_real_name','user_options','user_touched', 'user_token' ), |
| 706 | + 'user_real_name','user_options','user_touched', 'user_token', 'user_registration' ), |
704 | 707 | array( 'user_id' => $this->mId ), $fname ); |
705 | 708 | |
706 | 709 | if ( $s !== false ) { |
— | — | @@ -712,6 +715,7 @@ |
713 | 716 | $this->decodeOptions( $s->user_options ); |
714 | 717 | $this->mTouched = wfTimestamp(TS_MW,$s->user_touched); |
715 | 718 | $this->mToken = $s->user_token; |
| 719 | + $this->mRegistration = wfTimestamp( TS_MW, $s->user_registration ); |
716 | 720 | |
717 | 721 | $res = $dbr->select( 'user_groups', |
718 | 722 | array( 'ug_group' ), |
— | — | @@ -721,7 +725,15 @@ |
722 | 726 | while( $row = $dbr->fetchObject( $res ) ) { |
723 | 727 | $this->mGroups[] = $row->ug_group; |
724 | 728 | } |
725 | | - $effectiveGroups = array_merge( array( '*', 'user' ), $this->mGroups ); |
| 729 | + $implicitGroups = array( '*', 'user' ); |
| 730 | + |
| 731 | + global $wgAutoConfirmAge; |
| 732 | + $accountAge = time() - wfTimestampOrNull( TS_UNIX, $this->mRegistration ); |
| 733 | + if( $accountAge >= $wgAutoConfirmAge ) { |
| 734 | + $implicitGroups[] = 'autoconfirmed'; |
| 735 | + } |
| 736 | + |
| 737 | + $effectiveGroups = array_merge( $implicitGroups, $this->mGroups ); |
726 | 738 | $this->mRights = $this->getGroupPermissions( $effectiveGroups ); |
727 | 739 | } |
728 | 740 | |
— | — | @@ -1392,7 +1404,8 @@ |
1393 | 1405 | 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ), |
1394 | 1406 | 'user_real_name' => $this->mRealName, |
1395 | 1407 | 'user_options' => $this->encodeOptions(), |
1396 | | - 'user_token' => $this->mToken |
| 1408 | + 'user_token' => $this->mToken, |
| 1409 | + 'user_registration' => $dbw->timestamp( $this->mRegistration ), |
1397 | 1410 | ), $fname |
1398 | 1411 | ); |
1399 | 1412 | $this->mId = $dbw->insertId(); |
— | — | @@ -1526,7 +1539,8 @@ |
1527 | 1540 | * @return bool True if it is a newbie. |
1528 | 1541 | */ |
1529 | 1542 | function isNewbie() { |
1530 | | - return $this->isAnon() || $this->mId > User::getMaxID() * 0.99 && !$this->isAllowed( 'delete' ) && !$this->isBot(); |
| 1543 | + return !$this->isAllowed( 'autoconfirmed' ); |
| 1544 | + /return $this->isAnon() || $this->mId > User::getMaxID() * 0.99 && !$this->isAllowed( 'delete' ) && !$this->isBot(); |
1531 | 1545 | } |
1532 | 1546 | |
1533 | 1547 | /** |
— | — | @@ -1811,9 +1825,9 @@ |
1812 | 1826 | global $wgGroupPermissions; |
1813 | 1827 | return array_diff( |
1814 | 1828 | array_keys( $wgGroupPermissions ), |
1815 | | - array( '*', 'user' ) ); |
| 1829 | + array( '*', 'user', 'autoconfirmed' ) ); |
1816 | 1830 | } |
1817 | | - |
| 1831 | + |
1818 | 1832 | } |
1819 | 1833 | |
1820 | 1834 | ?> |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -1582,166 +1582,93 @@ |
1583 | 1583 | } |
1584 | 1584 | |
1585 | 1585 | /** |
1586 | | - * protect a page |
| 1586 | + * action=protect handler |
1587 | 1587 | */ |
1588 | | - function protect( $limit = 'sysop' ) { |
| 1588 | + function protect() { |
| 1589 | + require_once 'ProtectionForm.php'; |
| 1590 | + $form = new ProtectionForm( $this ); |
| 1591 | + $form->show(); |
| 1592 | + } |
| 1593 | + |
| 1594 | + /** |
| 1595 | + * action=unprotect handler (alias) |
| 1596 | + */ |
| 1597 | + function unprotect() { |
| 1598 | + $this->protect(); |
| 1599 | + } |
| 1600 | + |
| 1601 | + /** |
| 1602 | + * Update the article's restriction field, and leave a log entry. |
| 1603 | + * |
| 1604 | + * @param array $limit set of restriction keys |
| 1605 | + * @param string $reason |
| 1606 | + * @return bool true on success |
| 1607 | + */ |
| 1608 | + function updateRestrictions( $limit = array(), $reason = '' ) { |
1589 | 1609 | global $wgUser, $wgOut, $wgRequest; |
1590 | 1610 | |
1591 | | - if ( ! $wgUser->isAllowed('protect') ) { |
1592 | | - $wgOut->sysopRequired(); |
1593 | | - return; |
| 1611 | + if ( !$wgUser->isAllowed( 'protect' ) ) { |
| 1612 | + return false; |
1594 | 1613 | } |
1595 | 1614 | |
1596 | | - / bug 2261 |
1597 | | - if ( $this->mTitle->isProtected() && $limit == 'sysop' ) { |
1598 | | - $this->view(); |
1599 | | - return; |
| 1615 | + if( wfReadOnly() ) { |
| 1616 | + return false; |
1600 | 1617 | } |
1601 | 1618 | |
1602 | | - if ( wfReadOnly() ) { |
1603 | | - $wgOut->readOnlyPage(); |
1604 | | - return; |
1605 | | - } |
1606 | | - |
1607 | 1619 | $id = $this->mTitle->getArticleID(); |
1608 | 1620 | if ( 0 == $id ) { |
1609 | | - $wgOut->fatalError( wfMsg( 'badarticleerror' ) ); |
1610 | | - return; |
| 1621 | + return false; |
1611 | 1622 | } |
1612 | 1623 | |
1613 | | - $confirm = $wgRequest->wasPosted() && |
1614 | | - $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); |
1615 | | - $moveonly = $wgRequest->getBool( 'wpMoveOnly' ); |
1616 | | - $reason = $wgRequest->getText( 'wpReasonProtect' ); |
| 1624 | + $flat = Article::flattenRestrictions( $limit ); |
| 1625 | + $protecting = ($flat != ''); |
| 1626 | + |
| 1627 | + if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, |
| 1628 | + $limit, $reason ) ) ) { |
1617 | 1629 | |
1618 | | - if ( $confirm ) { |
1619 | 1630 | $dbw =& wfGetDB( DB_MASTER ); |
1620 | 1631 | $dbw->update( 'page', |
1621 | 1632 | array( /* SET */ |
1622 | 1633 | 'page_touched' => $dbw->timestamp(), |
1623 | | - 'page_restrictions' => (string)$limit |
| 1634 | + 'page_restrictions' => $flat |
1624 | 1635 | ), array( /* WHERE */ |
1625 | 1636 | 'page_id' => $id |
1626 | 1637 | ), 'Article::protect' |
1627 | 1638 | ); |
1628 | 1639 | |
1629 | | - $restrictions = "move=" . $limit; |
1630 | | - if( !$moveonly ) { |
1631 | | - $restrictions .= ":edit=" . $limit; |
1632 | | - } |
1633 | | - if (wfRunHooks('ArticleProtect', array(&$this, &$wgUser, $limit == 'sysop', $reason, $moveonly))) { |
| 1640 | + wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, |
| 1641 | + $limit, $reason ) ); |
1634 | 1642 | |
1635 | | - $dbw =& wfGetDB( DB_MASTER ); |
1636 | | - $dbw->update( 'page', |
1637 | | - array( /* SET */ |
1638 | | - 'page_touched' => $dbw->timestamp(), |
1639 | | - 'page_restrictions' => $restrictions |
1640 | | - ), array( /* WHERE */ |
1641 | | - 'page_id' => $id |
1642 | | - ), 'Article::protect' |
1643 | | - ); |
1644 | | - |
1645 | | - wfRunHooks('ArticleProtectComplete', array(&$this, &$wgUser, $limit == 'sysop', $reason, $moveonly)); |
1646 | | - |
1647 | | - $log = new LogPage( 'protect' ); |
1648 | | - if ( $limit === '' ) { |
1649 | | - $log->addEntry( 'unprotect', $this->mTitle, $reason ); |
1650 | | - } else { |
1651 | | - $log->addEntry( 'protect', $this->mTitle, $reason ); |
1652 | | - } |
1653 | | - $wgOut->redirect( $this->mTitle->getFullURL() ); |
| 1643 | + $log = new LogPage( 'protect' ); |
| 1644 | + if( $protecting ) { |
| 1645 | + $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$flat]" ) ); |
| 1646 | + } else { |
| 1647 | + $log->addEntry( 'unprotect', $this->mTitle, $reason ); |
1654 | 1648 | } |
1655 | | - return; |
1656 | | - } else { |
1657 | | - return $this->confirmProtect( '', '', $limit ); |
1658 | 1649 | } |
| 1650 | + return true; |
1659 | 1651 | } |
1660 | | - |
| 1652 | + |
1661 | 1653 | /** |
1662 | | - * Output protection confirmation dialog |
| 1654 | + * Take an array of page restrictions and flatten it to a string |
| 1655 | + * suitable for insertion into the page_restrictions field. |
| 1656 | + * @param array $limit |
| 1657 | + * @return string |
| 1658 | + * @access private |
1663 | 1659 | */ |
1664 | | - function confirmProtect( $par, $reason, $limit = 'sysop' ) { |
1665 | | - global $wgOut, $wgUser; |
1666 | | - |
1667 | | - wfDebug( "Article::confirmProtect\n" ); |
1668 | | - |
1669 | | - $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); |
1670 | | - $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
1671 | | - |
1672 | | - $check = ''; |
1673 | | - $protcom = ''; |
1674 | | - $moveonly = ''; |
1675 | | - |
1676 | | - if ( $limit === '' ) { |
1677 | | - $wgOut->setPageTitle( wfMsg( 'confirmunprotect' ) ); |
1678 | | - $wgOut->setSubtitle( wfMsg( 'unprotectsub', $sub ) ); |
1679 | | - $wgOut->addWikiText( wfMsg( 'confirmunprotecttext' ) ); |
1680 | | - $protcom = htmlspecialchars( wfMsg( 'unprotectcomment' ) ); |
1681 | | - $formaction = $this->mTitle->escapeLocalURL( 'action=unprotect' . $par ); |
1682 | | - } else { |
1683 | | - $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); |
1684 | | - $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); |
1685 | | - $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); |
1686 | | - $moveonly = wfMsg( 'protectmoveonly' ) ; / add it using addWikiText to prevent xss. bug:3991 |
1687 | | - $protcom = htmlspecialchars( wfMsg( 'protectcomment' ) ); |
1688 | | - $formaction = $this->mTitle->escapeLocalURL( 'action=protect' . $par ); |
| 1660 | + function flattenRestrictions( $limit ) { |
| 1661 | + if( !is_array( $limit ) ) { |
| 1662 | + wfDebugDieBacktrace( 'Article::flattenRestrictions given non-array restriction set' ); |
1689 | 1663 | } |
1690 | | - |
1691 | | - $confirm = htmlspecialchars( wfMsg( 'protectpage' ) ); |
1692 | | - $token = htmlspecialchars( $wgUser->editToken() ); |
1693 | | - |
1694 | | - $wgOut->addHTML( " |
1695 | | -<form id='protectconfirm' method='post' action=\"{$formaction}\"> |
1696 | | - <table border='0'> |
1697 | | - <tr> |
1698 | | - <td align='right'> |
1699 | | - <label for='wpReasonProtect'>{$protcom}:</label> |
1700 | | - </td> |
1701 | | - <td align='left'> |
1702 | | - <input type='text' size='60' name='wpReasonProtect' id='wpReasonProtect' value=\"" . htmlspecialchars( $reason ) . "\" /> |
1703 | | - </td> |
1704 | | - </tr>" ); |
1705 | | - if($moveonly != '') { |
1706 | | - $wgOut->AddHTML( " |
1707 | | - <tr> |
1708 | | - <td align='right'> |
1709 | | - <input type='checkbox' name='wpMoveOnly' value='1' id='wpMoveOnly' /> |
1710 | | - </td> |
1711 | | - <td align='left'> |
1712 | | - <label for='wpMoveOnly'> "); |
1713 | | - $wgOut->addWikiText( $moveonly ); / bug 3991 |
1714 | | - $wgOut->addHTML( " |
1715 | | - </label> |
1716 | | - </td> |
1717 | | - </tr> " ); |
| 1664 | + $bits = array(); |
| 1665 | + foreach( $limit as $action => $restrictions ) { |
| 1666 | + if( $restrictions != '' ) { |
| 1667 | + $bits[] = "$action=$restrictions"; |
| 1668 | + } |
1718 | 1669 | } |
1719 | | - $wgOut->addHTML( " |
1720 | | - <tr> |
1721 | | - <td> </td> |
1722 | | - <td> |
1723 | | - <input type='submit' name='wpConfirmProtectB' value=\"{$confirm}\" /> |
1724 | | - </td> |
1725 | | - </tr> |
1726 | | - </table> |
1727 | | - <input type='hidden' name='wpEditToken' value=\"{$token}\" /> |
1728 | | -</form>" ); |
1729 | | - |
1730 | | - $wgOut->returnToMain( false ); |
| 1670 | + return implode( ':', $bits ); |
1731 | 1671 | } |
1732 | 1672 | |
1733 | | - /** |
1734 | | - * Unprotect the pages |
1735 | | - */ |
1736 | | - function unprotect() { |
1737 | | - / bug 2261 |
1738 | | - if ( $this->mTitle->isProtected() ) { |
1739 | | - return $this->protect( '' ); |
1740 | | - } else { |
1741 | | - $this->view(); |
1742 | | - return; |
1743 | | - } |
1744 | | - } |
1745 | | - |
1746 | 1673 | /* |
1747 | 1674 | * UI entry point for page deletion |
1748 | 1675 | */ |
Index: trunk/phase3/includes/SpecialLog.php |
— | — | @@ -275,7 +275,6 @@ |
276 | 276 | |
277 | 277 | function doShowList( &$out, $result ) { |
278 | 278 | / Rewind result pointer and go through it again, making the HTML |
279 | | - $html=''; |
280 | 279 | if ($this->numResults > 0) { |
281 | 280 | $html = "\n<ul>\n"; |
282 | 281 | $result->seek( 0 ); |
— | — | @@ -283,9 +282,11 @@ |
284 | 283 | $html .= $this->logLine( $s ); |
285 | 284 | } |
286 | 285 | $html .= "\n</ul>\n"; |
| 286 | + $out->addHTML( $html ); |
| 287 | + } else { |
| 288 | + $out->addWikiText( wfMsg( 'logempty' ) ); |
287 | 289 | } |
288 | 290 | $result->free(); |
289 | | - $out->addHTML( $html ); |
290 | 291 | } |
291 | 292 | |
292 | 293 | /** |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -772,12 +772,14 @@ |
773 | 773 | */ |
774 | 774 | $wgGroupPermissions = array(); |
775 | 775 | |
| 776 | +/ Implicit group for all visitors |
776 | 777 | $wgGroupPermissions['*' ]['createaccount'] = true; |
777 | 778 | $wgGroupPermissions['*' ]['read'] = true; |
778 | 779 | $wgGroupPermissions['*' ]['edit'] = true; |
779 | 780 | $wgGroupPermissions['*' ]['createpage'] = true; |
780 | 781 | $wgGroupPermissions['*' ]['createtalk'] = true; |
781 | 782 | |
| 783 | +/ Implicit group for all logged-in accounts |
782 | 784 | $wgGroupPermissions['user' ]['move'] = true; |
783 | 785 | $wgGroupPermissions['user' ]['read'] = true; |
784 | 786 | $wgGroupPermissions['user' ]['edit'] = true; |
— | — | @@ -787,8 +789,15 @@ |
788 | 790 | $wgGroupPermissions['user' ]['reupload'] = true; |
789 | 791 | $wgGroupPermissions['user' ]['reupload-shared'] = true; |
790 | 792 | |
| 793 | +/ Implicit group for accounts that pass $wgAutoConfirmAge |
| 794 | +$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true; |
| 795 | + |
| 796 | +/ Users with bot privilege can have their edits hidden |
| 797 | +/ from various log pages by default |
791 | 798 | $wgGroupPermissions['bot' ]['bot'] = true; |
| 799 | +$wgGroupPermissions['bot' ]['autoconfirmed'] = true; |
792 | 800 | |
| 801 | +/ Most extra permission abilities go to this group |
793 | 802 | $wgGroupPermissions['sysop']['block'] = true; |
794 | 803 | $wgGroupPermissions['sysop']['createaccount'] = true; |
795 | 804 | $wgGroupPermissions['sysop']['delete'] = true; |
— | — | @@ -803,7 +812,9 @@ |
804 | 813 | $wgGroupPermissions['sysop']['reupload'] = true; |
805 | 814 | $wgGroupPermissions['sysop']['reupload-shared'] = true; |
806 | 815 | $wgGroupPermissions['sysop']['unwatchedpages'] = true; |
| 816 | +$wgGroupPermissions['sysop']['autoconfirmed'] = true; |
807 | 817 | |
| 818 | +/ Permission to change users' group assignments |
808 | 819 | $wgGroupPermissions['bureaucrat']['userrights'] = true; |
809 | 820 | |
810 | 821 | /** |
— | — | @@ -815,7 +826,36 @@ |
816 | 827 | # $wgGroupPermissions['developer']['siteadmin'] = true; |
817 | 828 | |
818 | 829 | |
| 830 | +/** |
| 831 | + * Set of available actions that can be restricted via Special:Protect |
| 832 | + * You probably shouldn't change this. |
| 833 | + */ |
| 834 | +$wgRestrictionTypes = array( 'edit', 'move' ); |
819 | 835 | |
| 836 | +/** |
| 837 | + * Set of permission keys that can be selected via Special:Protect. |
| 838 | + * 'autoconfirm' allows all registerd users if $wgAutoConfirmAge is 0. |
| 839 | + */ |
| 840 | +$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ); |
| 841 | + |
| 842 | + |
| 843 | +/** |
| 844 | + * Number of seconds an account is required to age before |
| 845 | + * it's given the implicit 'autoconfirm' group membership. |
| 846 | + * This can be used to limit privileges of new accounts. |
| 847 | + * |
| 848 | + * Accounts created by earlier versions of the software |
| 849 | + * may not have a recorded creation date, and will always |
| 850 | + * be considered to pass the age test. |
| 851 | + * |
| 852 | + * When left at 0, all registered accounts will pass. |
| 853 | + */ |
| 854 | +$wgAutoConfirmAge = 0; |
| 855 | +/$wgAutoConfirmAge = 600; / ten minutes |
| 856 | +/$wgAutoConfirmAge = 3600*24; / one day |
| 857 | + |
| 858 | + |
| 859 | + |
820 | 860 | # Proxy scanner settings |
821 | 861 | # |
822 | 862 | |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -341,6 +341,10 @@ |
342 | 342 | * (bug 3424) Update page_touched for category members on category page creation |
343 | 343 | * (bug 4108, 4336) Remove trailing whitespace from various messages, which |
344 | 344 | mucks up message updating to create dupe entries |
| 345 | +* (bug 1735) Revamped protection interface |
| 346 | +* (bug 675) Add page protection level for unregistered/new accounts |
| 347 | +* User::isNewbie now uses the registration date and $wgAutoconfirmAge |
| 348 | +* Log views show message when no matches |
345 | 349 | |
346 | 350 | |
347 | 351 | === Caveats === |
Index: trunk/phase3/languages/Language.php |
— | — | @@ -1208,7 +1208,9 @@ |
1209 | 1209 | 'log' => 'Logs', |
1210 | 1210 | 'alllogstext' => 'Combined display of upload, deletion, protection, blocking, and sysop logs. |
1211 | 1211 | You can narrow down the view by selecting a log type, the user name, or the affected page.', |
| 1212 | +'logempty' => 'No matching items in log.', |
1212 | 1213 | |
| 1214 | + |
1213 | 1215 | # Special:Allpages |
1214 | 1216 | 'nextpage' => 'Next page ($1)', |
1215 | 1217 | 'allpagesfrom' => 'Display pages starting at:', |
— | — | @@ -1376,7 +1378,16 @@ |
1377 | 1379 | 'confirmunprotecttext' => 'Do you really want to unprotect this page?', |
1378 | 1380 | 'confirmunprotect' => 'Confirm unprotection', |
1379 | 1381 | 'unprotectcomment' => 'Reason for unprotecting', |
| 1382 | +'protect-unchain' => 'Unlock move permissions', |
| 1383 | +'protect-text' => 'You may view and change the protection level here for the page [[$1]]. |
| 1384 | +Please be sure you are following the [[Project:Protected page|project guidelines]].', |
| 1385 | +'protect-viewtext' => 'Your account does not have permission to change |
| 1386 | +page protection levels. Here are the current settings for the page [[$1]]:', |
| 1387 | +'protect-default' => '(default)', |
| 1388 | +'protect-level-autoconfirmed' => 'Block unregistered users', |
| 1389 | +'protect-level-sysop' => 'Sysops only', |
1380 | 1390 | |
| 1391 | + |
1381 | 1392 | # Undelete |
1382 | 1393 | 'undelete' => 'View deleted pages', |
1383 | 1394 | 'undeletepage' => 'View and restore deleted pages', |