Index: trunk/extensions/CentralAuth/WikiMap.php |
— | — | @@ -7,10 +7,14 @@ |
8 | 8 | class WikiMap { |
9 | 9 | static function byDatabase( $dbname ) { |
10 | 10 | global $wgConf, $IP; |
| 11 | + static $initialiseSettingsDone = false; |
11 | 12 | |
12 | 13 | // This is a damn dirty hack |
13 | | - if( file_exists( "$IP/InitialiseSettings.php" ) ) { |
14 | | - require_once "$IP/InitialiseSettings.php"; |
| 14 | + if ( !$initialiseSettingsDone ) { |
| 15 | + $initialiseSettingsDone = true; |
| 16 | + if( file_exists( "$IP/InitialiseSettings.php" ) ) { |
| 17 | + require_once "$IP/InitialiseSettings.php"; |
| 18 | + } |
15 | 19 | } |
16 | 20 | |
17 | 21 | list( $major, $minor ) = $wgConf->siteFromDB( $dbname ); |
— | — | @@ -41,12 +45,13 @@ |
42 | 46 | } |
43 | 47 | |
44 | 48 | function getHostname() { |
45 | | - if( substr( $this->mServer, 0, 7 ) === 'http://' ) { |
46 | | - return substr( $this->mServer, 7 ); |
47 | | - } else { |
48 | | - throw new MWException( "wtf" ); |
| 49 | + $prefixes = array( 'http://', 'https://' ); |
| 50 | + foreach ( $prefixes as $prefix ) { |
| 51 | + if ( substr( $this->mServer, 0, strlen( $prefix ) ) ) { |
| 52 | + return substr( $this->mServer, strlen( $prefix ) ); |
| 53 | + } |
49 | 54 | } |
50 | | - // Q: Could it happen that they're using https:// ? |
| 55 | + throw new MWException( "Invalid hostname for wiki {$this->mMinor}.{$this->mMajor}" ); |
51 | 56 | } |
52 | 57 | |
53 | 58 | /** |
— | — | @@ -68,8 +73,7 @@ |
69 | 74 | |
70 | 75 | function getUrl( $page ) { |
71 | 76 | return |
72 | | - 'http://' . |
73 | | - $this->getHostname() . |
| 77 | + $this->mServer . |
74 | 78 | $this->getLocalUrl( $page ); |
75 | 79 | } |
76 | 80 | } |
Index: trunk/extensions/CentralAuth/CentralAuth.i18n.php |
— | — | @@ -137,6 +137,18 @@ |
138 | 138 | 'centralauth-admin-merge' => 'Merge selected', |
139 | 139 | 'centralauth-admin-bad-input' => 'Invalid merge selection', |
140 | 140 | 'centralauth-admin-none-selected' => 'No accounts selected to modify.', |
| 141 | + 'centralauth-admin-already-unmerged' => 'Skipping $1, already unmerged', |
| 142 | + 'centralauth-admin-unmerge-success' => 'Successfully unmerged $1 {{PLURAL:$2|account|accounts}}', |
| 143 | + 'centralauth-admin-delete-title' => 'Delete account', |
| 144 | + 'centralauth-admin-delete-description' => 'Deleting the global account will delete any |
| 145 | +global preferences, unattach all local accounts, and leave the global name free for another user to |
| 146 | +take. All local accounts will continue to exist. The passwords for local accounts created before |
| 147 | +the merge will revert to their pre-merge values.', |
| 148 | + 'centralauth-admin-delete-button' => 'Delete this account', |
| 149 | + 'centralauth-admin-delete-success' => 'Successfully deleted the the global account for "<nowiki>$1</nowiki>"', |
| 150 | + 'centralauth-admin-nonexistent' => 'There is no global account for "<nowiki>$1</nowiki>"', |
| 151 | + 'centralauth-admin-delete-nonexistent' => 'Error: the global account "<nowiki>$1</nowiki>" does not exist.', |
| 152 | + 'centralauth-token-mismatch' => 'Sorry, we could not process your form submission due to a loss of session data.', |
141 | 153 | |
142 | 154 | // Pretty timespan |
143 | 155 | 'centralauth-seconds-ago' => '$1 {{PLURAL:$1|second|seconds}} ago', |
— | — | @@ -162,6 +174,7 @@ |
163 | 175 | 'centralauth-renameuser-exists' => "<div class=\"errorbox\">Cannot rename user $2 as this username is reserved for a global account.</div>", |
164 | 176 | |
165 | 177 | // Other messages |
| 178 | + 'centralauth-invalid-wiki' => 'No such wiki DB: $1', |
166 | 179 | 'centralauth-account-exists' => 'Cannot create account: the requested username is already taken in the unified login system.', |
167 | 180 | ); |
168 | 181 | |
Index: trunk/extensions/CentralAuth/CentralAuthUser.php |
— | — | @@ -574,50 +574,78 @@ |
575 | 575 | return $dblist; |
576 | 576 | } |
577 | 577 | |
578 | | - public function adminAttach( $list, &$migrated=null, &$remaining=null ) { |
| 578 | + /** |
| 579 | + * Unattach a list of local accounts from the global account |
| 580 | + * @param array $list List of wiki names |
| 581 | + * @return Status |
| 582 | + */ |
| 583 | + public function adminUnattach( $list ) { |
| 584 | + if ( !count( $list ) ) { |
| 585 | + return Status::newFatal( 'centralauth-admin-none-selected' ); |
| 586 | + } |
| 587 | + $status = new Status; |
579 | 588 | $valid = $this->validateList( $list ); |
580 | | - $unattached = $this->queryUnattached(); |
| 589 | + $invalid = array_diff( $list, $valid ); |
| 590 | + foreach ( $invalid as $wikiName ) { |
| 591 | + $status->error( 'centralauth-invalid-wiki', $wikiName ); |
| 592 | + $status->failCount++; |
| 593 | + } |
| 594 | + |
| 595 | + $invalidCount = count( $list ) - count( $valid ); |
| 596 | + $missingCount = 0; |
| 597 | + $dbcw = self::getCentralDB(); |
581 | 598 | |
582 | | - $migrated = array(); |
583 | | - $remaining = array(); |
584 | | - |
585 | | - foreach( $unattached as $row ) { |
586 | | - if( in_array( $row['dbName'], $valid ) ) { |
587 | | - $this->attach( $row['dbName'], 'admin' ); |
588 | | - $migrated[] = $row['dbName']; |
589 | | - } else { |
590 | | - $remaining[] = $row['dbName']; |
| 599 | + foreach ( $valid as $wikiName ) { |
| 600 | + # Delete the user from the central localuser table |
| 601 | + $dbcw->delete( self::tableName( 'localuser' ), |
| 602 | + array( |
| 603 | + 'lu_name' => $this->mName, |
| 604 | + 'lu_dbname' => $wikiName ), |
| 605 | + __METHOD__ ); |
| 606 | + if ( !$dbcw->affectedRows() ) { |
| 607 | + $wiki = WikiMap::byDatabase( $wikiName ); |
| 608 | + $status->error( 'centralauth-admin-already-unmerged', $wiki->getDisplayName() ); |
| 609 | + $status->failCount++; |
| 610 | + continue; |
591 | 611 | } |
592 | | - } |
593 | 612 | |
594 | | - return count( $migrated ) == count( $valid ); |
595 | | - } |
| 613 | + # Touch the local user row |
| 614 | + $lb = wfGetLB( $wikiName ); |
| 615 | + $dblw = $lb->getConnection( DB_MASTER, array(), $wikiName ); |
| 616 | + $dblw->update( 'user', array( 'user_touched' => wfTimestampNow() ), |
| 617 | + array( 'user_name' => $this->mName ), __METHOD__ ); |
| 618 | + $lb->reuseConnection( $dblw ); |
596 | 619 | |
597 | | - public function adminUnattach( $list, &$migrated=null, &$remaining=null ) { |
598 | | - $valid = $this->validateList( $list ); |
| 620 | + $status->successCount++; |
| 621 | + } |
599 | 622 | |
600 | | - $dbw = self::getCentralDB(); |
601 | | - $dbw->delete( self::tableName( 'localuser' ), |
602 | | - array( |
603 | | - 'lu_name' => $this->mName, |
604 | | - 'lu_dbname' => $valid ), |
605 | | - __METHOD__ ); |
606 | | - |
607 | | - // FIXME: touch remote-database user accounts |
608 | | - |
609 | | - // FIXME: proper... stuff |
610 | | - $migrated = array(); |
611 | | - $remaining = $list; |
612 | | - |
613 | 623 | global $wgDBname; |
614 | 624 | if( in_array( $wgDBname, $valid ) ) { |
615 | 625 | $this->resetState(); |
616 | 626 | } |
617 | 627 | |
618 | | - return count( $list ) == count( $valid ); |
| 628 | + return $status; |
619 | 629 | } |
620 | 630 | |
621 | 631 | /** |
| 632 | + * Delete a global account |
| 633 | + */ |
| 634 | + function adminDelete() { |
| 635 | + $dbw = self::getCentralDB(); |
| 636 | + $dbw->begin(); |
| 637 | + # Delete and lock the globaluser row |
| 638 | + $dbw->delete( 'globaluser', array( 'gu_name' => $this->mName ), __METHOD__ ); |
| 639 | + if ( !$dbw->affectedRows() ) { |
| 640 | + $dbw->commit(); |
| 641 | + return Status::newFatal( 'centralauth-admin-delete-nonexistent', $this->mName ); |
| 642 | + } |
| 643 | + # Delete the localuser rows |
| 644 | + $dbw->delete( 'localuser', array( 'lu_name' => $this->mName ), __METHOD__ ); |
| 645 | + $dbw->commit(); |
| 646 | + return Status::newGood(); |
| 647 | + } |
| 648 | + |
| 649 | + /** |
622 | 650 | * Add a local account record for the given wiki to the central database. |
623 | 651 | * @param string $dbname |
624 | 652 | * @param int $localid |
Index: trunk/extensions/CentralAuth/SpecialCentralAuth.php |
— | — | @@ -36,7 +36,7 @@ |
37 | 37 | str_replace( '_', ' ', |
38 | 38 | $wgRequest->getText( 'target', $subpage ) ) ); |
39 | 39 | |
40 | | - $this->mAttemptMerge = $wgRequest->wasPosted(); |
| 40 | + $this->mPosted = $wgRequest->wasPosted(); |
41 | 41 | $this->mMethod = $wgRequest->getVal( 'wpMethod' ); |
42 | 42 | $this->mPassword = $wgRequest->getVal( 'wpPassword' ); |
43 | 43 | $this->mDatabases = (array)$wgRequest->getArray( 'wpWikis' ); |
— | — | @@ -52,41 +52,67 @@ |
53 | 53 | // did / did not merge some accounts |
54 | 54 | // do / don't have more accounts to merge |
55 | 55 | |
| 56 | + if ( $this->mUserName === '' ) { |
| 57 | + # First time through |
| 58 | + $this->showUsernameForm(); |
| 59 | + return; |
| 60 | + } |
| 61 | + |
56 | 62 | $globalUser = new CentralAuthUser( $this->mUserName ); |
57 | | - $merged = $remainder = array(); |
58 | | - $this->showUsernameForm(); |
59 | 63 | |
60 | | - if( $this->mAttemptMerge ) { |
61 | | - if( empty( $this->mDatabases ) ) { |
62 | | - $this->showError( wfMsg( 'centralauth-admin-none-selected' ) ); |
63 | | - } elseif( $this->mMethod == 'admin' ) { |
64 | | - $ok = $globalUser->adminAttach( |
65 | | - $this->mDatabases, |
66 | | - $merged, |
67 | | - $remainder ); |
| 64 | + if ( !$globalUser->exists() ) { |
| 65 | + $this->showError( wfMsgNoTrans( 'centralauth-admin-nonexistent', $this->mUserName ) ); |
| 66 | + $this->showUsernameForm(); |
| 67 | + return; |
| 68 | + } |
| 69 | + |
| 70 | + $deleted = false; |
| 71 | + |
| 72 | + if( $this->mPosted ) { |
| 73 | + if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { |
| 74 | + $this->showError( wfMsg( 'centralauth-token-mismatch' ) ); |
68 | 75 | } elseif( $this->mMethod == 'unmerge' ) { |
69 | | - $ok = $globalUser->adminUnattach( |
70 | | - $this->mDatabases, |
71 | | - $merged, |
72 | | - $remainder ); |
| 76 | + $status = $globalUser->adminUnattach( $this->mDatabases ); |
| 77 | + if ( !$status->isGood() ) { |
| 78 | + $this->showError( $status->getWikiText() ); |
| 79 | + } else { |
| 80 | + global $wgLang; |
| 81 | + $this->showSuccess( wfMsgNoTrans( 'centralauth-admin-unmerge-success', |
| 82 | + $wgLang->formatNum( $status->successCount ), |
| 83 | + $status->successCount ) ); |
| 84 | + } |
| 85 | + } elseif ( $this->mMethod == 'delete' ) { |
| 86 | + $status = $globalUser->adminDelete(); |
| 87 | + if ( !$status->isGood() ) { |
| 88 | + $this->showError( $status->getWikiText() ); |
| 89 | + } else { |
| 90 | + global $wgLang; |
| 91 | + $this->showSuccess( wfMsgNoTrans( 'centralauth-admin-delete-success', $this->mUserName ) ); |
| 92 | + $deleted = true; |
| 93 | + } |
73 | 94 | } else { |
74 | 95 | $this->showError( wfMsg( 'centralauth-admin-bad-input' ) ); |
75 | 96 | } |
76 | 97 | |
77 | | - // if (! $ok) { .... |
78 | | - } else { |
79 | | - $merged = $globalUser->listAttached(); |
80 | | - $remainder = $globalUser->listUnattached(); |
81 | 98 | } |
82 | 99 | |
83 | | - $this->showInfo(); |
| 100 | + $this->showUsernameForm(); |
| 101 | + if ( !$deleted ) { |
| 102 | + $this->showInfo(); |
| 103 | + $this->showDeleteForm(); |
| 104 | + } |
84 | 105 | } |
85 | 106 | |
86 | 107 | function showError( $message ) { |
87 | 108 | global $wgOut; |
88 | | - $wgOut->addWikiText( "<div class='error'>$message</div>" ); |
| 109 | + $wgOut->addWikiText( "<div class='error'>\n$message</div>" ); |
89 | 110 | } |
90 | 111 | |
| 112 | + function showSuccess( $message ) { |
| 113 | + global $wgOut; |
| 114 | + $wgOut->addWikiText( "<div class='success'>\n$message</div>" ); |
| 115 | + } |
| 116 | + |
91 | 117 | function showUsernameForm() { |
92 | 118 | global $wgOut, $wgScript; |
93 | 119 | $wgOut->addHtml( |
— | — | @@ -168,11 +194,17 @@ |
169 | 195 | } |
170 | 196 | |
171 | 197 | function listRemainder( $list ) { |
172 | | - return $this->listForm( $list, 'listRemainingWikiItem', |
173 | | - 'admin', wfMsg( 'centralauth-admin-merge' ) ); |
| 198 | + ksort( $list ); |
| 199 | + $s = '<ul>'; |
| 200 | + foreach ( $list as $row ) { |
| 201 | + $s .= '<li>' . $this->foreignUserLink( $row['dbName'] ) . "</li>\n"; |
| 202 | + } |
| 203 | + $s .= '</ul>'; |
| 204 | + return $s; |
174 | 205 | } |
175 | 206 | |
176 | 207 | function listForm( $list, $listMethod, $action, $buttonText ) { |
| 208 | + global $wgUser; |
177 | 209 | ksort( $list ); |
178 | 210 | return |
179 | 211 | Xml::openElement( 'form', |
— | — | @@ -180,6 +212,7 @@ |
181 | 213 | 'method' => 'post', |
182 | 214 | 'action' => $this->getTitle( $this->mUserName )->getLocalUrl( 'action=submit' ) ) ) . |
183 | 215 | Xml::hidden( 'wpMethod', $action ) . |
| 216 | + Xml::hidden( 'wpEditToken', $wgUser->editToken() ) . |
184 | 217 | '<table>' . |
185 | 218 | '<thead>' . |
186 | 219 | $this->tableRow( 'th', |
— | — | @@ -212,16 +245,6 @@ |
213 | 246 | ); |
214 | 247 | } |
215 | 248 | |
216 | | - function listRemainingWikiItem( $row ) { |
217 | | - global $wgLang; |
218 | | - return $this->tableRow( 'td', |
219 | | - array( |
220 | | - $this->adminCheck( $row['dbName'] ), |
221 | | - $this->foreignUserLink( $row['dbName'] ), |
222 | | - ) |
223 | | - ); |
224 | | - } |
225 | | - |
226 | 249 | function tableRow( $element, $cols ) { |
227 | 250 | return "<tr><$element>" . |
228 | 251 | implode( "</$element><$element>", $cols ) . |
— | — | @@ -251,4 +274,20 @@ |
252 | 275 | return |
253 | 276 | Xml::check( 'wpWikis[]', false, array( 'value' => $dbname ) ); |
254 | 277 | } |
| 278 | + |
| 279 | + function showDeleteForm() { |
| 280 | + global $wgOut, $wgUser; |
| 281 | + $wgOut->addHtml( |
| 282 | + Xml::element( 'h2', array(), wfMsg( 'centralauth-admin-delete-title' ) ) . |
| 283 | + Xml::openElement( 'form', array( |
| 284 | + 'method' => 'POST', |
| 285 | + 'action' => $this->getTitle()->getFullUrl( 'target=' . urlencode( $this->mUserName ) ) ) ) . |
| 286 | + Xml::hidden( 'wpMethod', 'delete' ) . |
| 287 | + Xml::hidden( 'wpEditToken', $wgUser->editToken() ) . |
| 288 | + wfMsgExt( 'centralauth-admin-delete-description', 'parse' ) . |
| 289 | + '<p>' . |
| 290 | + Xml::submitButton( wfMsg( 'centralauth-admin-delete-button' ) ) . |
| 291 | + '</p>' . |
| 292 | + '</form>' ); |
| 293 | + } |
255 | 294 | } |