Index: trunk/extensions/ConfirmAccount/SpecialConfirmAccount.php |
— | — | @@ -0,0 +1,79 @@ |
| 2 | +<?php
|
| 3 | +#(c) Aaron Schulz
|
| 4 | +
|
| 5 | +if ( !defined( 'MEDIAWIKI' ) ) {
|
| 6 | + echo "ConfirmAccount extension\n";
|
| 7 | + exit( 1 ) ;
|
| 8 | +}
|
| 9 | +
|
| 10 | +# This extension needs email enabled!
|
| 11 | +# Otherwise users can't get their passwords...
|
| 12 | +if( !$wgEnableEmail ) {
|
| 13 | + echo "ConfirmAccount extension requeires \$wgEnableEmail set to true \n";
|
| 14 | + exit( 1 ) ;
|
| 15 | +}
|
| 16 | +
|
| 17 | +$wgExtensionCredits['specialpage'][] = array(
|
| 18 | + 'name' => 'Confirm user accounts',
|
| 19 | + 'description' => 'Gives bureaucrats the ability to confirm account requests',
|
| 20 | + 'author' => 'Aaron Schulz'
|
| 21 | +);
|
| 22 | +
|
| 23 | +# Set the person's bio as their userpage?
|
| 24 | +$wgMakeUserPageFromBio = true;
|
| 25 | +
|
| 26 | +$wgGroupPermissions['*']['createaccount'] = false;
|
| 27 | +$wgGroupPermissions['sysop']['createaccount'] = false;
|
| 28 | +$wgGroupPermissions['bureaucrat']['confirmaccount'] = true;
|
| 29 | +
|
| 30 | +$wgAccountRequestThrottle = 1;
|
| 31 | +
|
| 32 | +# Internationalisation
|
| 33 | +require_once( 'ConfirmAccount.i18n.php' );
|
| 34 | +
|
| 35 | +function efLoadConfirmAccountsMessages() {
|
| 36 | + global $wgMessageCache, $wgConfirmAccountMessages;
|
| 37 | +
|
| 38 | + foreach( $wgConfirmAccountMessages as $key => $value ) {
|
| 39 | + $wgMessageCache->addMessages( $wgConfirmAccountMessages[$key], $key );
|
| 40 | + }
|
| 41 | +}
|
| 42 | +
|
| 43 | +function efAddRequestLoginText( &$template ) {
|
| 44 | + efLoadConfirmAccountsMessages();
|
| 45 | +
|
| 46 | + $template->set( 'header', wfMsgExt('requestacount-loginnotice', array('parse') ) );
|
| 47 | +
|
| 48 | + return true;
|
| 49 | +}
|
| 50 | +
|
| 51 | +function efCheckIfAccountNameIsPending( &$user, &$abortError ) {
|
| 52 | + efLoadConfirmAccountsMessages();
|
| 53 | + # If an account is made with name X, and one is pending with name X
|
| 54 | + # we will have problems if the pending one is later confirmed
|
| 55 | + $dbw = wfGetDB( DB_MASTER );
|
| 56 | + $dup = $dbw->selectField( 'account_requests', '1',
|
| 57 | + array( 'acr_name' => $user->getName() ),
|
| 58 | + __METHOD__ );
|
| 59 | +
|
| 60 | + if ( $dup ) {
|
| 61 | + $abortError = wfMsgHtml('requestaccount-inuse');
|
| 62 | + }
|
| 63 | +
|
| 64 | + return true;
|
| 65 | +}
|
| 66 | +
|
| 67 | +# Register special page
|
| 68 | +if ( !function_exists( 'extAddSpecialPage' ) ) {
|
| 69 | + require( dirname(__FILE__) . '/../ExtensionFunctions.php' );
|
| 70 | +}
|
| 71 | +# Request an account
|
| 72 | +extAddSpecialPage( dirname(__FILE__) . '/ConfirmAccount_body.php', 'RequestAccount', 'RequestAccountPage' );
|
| 73 | +# Confirm accounts
|
| 74 | +extAddSpecialPage( dirname(__FILE__) . '/ConfirmAccount_body.php', 'ConfirmAccounts', 'ConfirmAccountsPage' );
|
| 75 | +
|
| 76 | +# Add notice of where to request an account
|
| 77 | +$wgHooks['UserCreateForm'][] = 'efAddRequestLoginText';
|
| 78 | +$wgHooks['UserLoginForm'][] = 'efAddRequestLoginText';
|
| 79 | +# Check for collisions
|
| 80 | +$wgHooks['AbortNewAccount'][] = 'efCheckIfAccountNameIsPending'; |
\ No newline at end of file |
Index: trunk/extensions/ConfirmAccount/ConfirmAccount.sql |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +-- (c) Aaron Schulz, 2007
|
| 3 | +
|
| 4 | +-- Table structure for table `Confirm account`
|
| 5 | +-- Replace /*$wgDBprefix*/ with the proper prefix
|
| 6 | +
|
| 7 | +-- This stores all of our reviews,
|
| 8 | +-- the corresponding tags are stored in the tag table
|
| 9 | +CREATE TABLE /*$wgDBprefix*/account_requests (
|
| 10 | + acr_id int unsigned NOT NULL auto_increment,
|
| 11 | + -- Usernames must be unique, must not be in the form of
|
| 12 | + -- an IP address. _Shouldn't_ allow slashes or case
|
| 13 | + -- conflicts. Spaces are allowed, and are _not_ converted
|
| 14 | + -- to underscores like titles. See the User::newFromName() for
|
| 15 | + -- the specific tests that usernames have to pass.
|
| 16 | + acr_name varchar(255) binary NOT NULL default '',
|
| 17 | + -- Optional 'real name' to be displayed in credit listings
|
| 18 | + acr_real_name varchar(255) binary NOT NULL default '',
|
| 19 | + -- Note: email should be restricted, not public info.
|
| 20 | + -- Same with passwords.
|
| 21 | + acr_email tinytext NOT NULL,
|
| 22 | + -- Initially NULL; when a user's e-mail address has been
|
| 23 | + -- validated by returning with a mailed token, this is
|
| 24 | + -- set to the current timestamp.
|
| 25 | + acr_email_authenticated binary(14) default NULL,
|
| 26 | + -- Randomly generated token created when the e-mail address
|
| 27 | + -- is set and a confirmation test mail sent.
|
| 28 | + acr_email_token binary(32),
|
| 29 | + -- Expiration date for the user_email_token
|
| 30 | + acr_email_token_expires binary(14),
|
| 31 | + -- Timestamp of account registration.
|
| 32 | + -- Accounts predating this schema addition may contain NULL.
|
| 33 | + acr_registration char(14) NOT NULL,
|
| 34 | + -- A little about this user
|
| 35 | + acr_bio mediumblob default '',
|
| 36 | + -- Private info for reviewers to look at when considering request
|
| 37 | + acr_notes mediumblob default '',
|
| 38 | + -- Links to recognize/identify this user, CSV, may not be public
|
| 39 | + acr_urls mediumblob default '',
|
| 40 | + -- IP address
|
| 41 | + acr_ip VARCHAR(255) NULL default '',
|
| 42 | +
|
| 43 | + PRIMARY KEY (acr_id),
|
| 44 | + UNIQUE KEY (acr_name),
|
| 45 | + INDEX (acr_registration),
|
| 46 | + INDEX (acr_email_token)
|
| 47 | +) TYPE=InnoDB; |
\ No newline at end of file |
Index: trunk/extensions/ConfirmAccount/ConfirmAccount.pg.sql |
— | — | @@ -0,0 +1,27 @@ |
| 2 | +-- (c) Aaron Schulz, 2007
|
| 3 | +
|
| 4 | +-- Table structure for table `Confirm account`
|
| 5 | +-- Replace /*$wgDBprefix*/ with the proper prefix
|
| 6 | +
|
| 7 | +BEGIN;
|
| 8 | +
|
| 9 | +CREATE SEQUENCE account_requests_acr_id_seq;
|
| 10 | +CREATE TABLE account_requests (
|
| 11 | + acr_id INTEGER NOT NULL DEFAULT nextval('account_requests_acr_id_seq'),
|
| 12 | + acr_name TEXT NOT NULL UNIQUE,
|
| 13 | + acr_real_name TEXT,
|
| 14 | + acr_email TEXT,
|
| 15 | + acr_email_token CHAR(32),
|
| 16 | + acr_email_token_expires TIMESTAMPTZ,
|
| 17 | + acr_email_authenticated TIMESTAMPTZ,
|
| 18 | + acr_registration TIMESTAMPTZ,
|
| 19 | + acr_bio TEXT,
|
| 20 | + acr_notes TEXT,
|
| 21 | + acr_url TEXT,
|
| 22 | + acr_ip CIDR
|
| 23 | +);
|
| 24 | +
|
| 25 | +CREATE INDEX acr_registration ON account_requests (acr_registration),
|
| 26 | +CREATE INDEX acr_email_token ON account_requests (acr_email_token);
|
| 27 | +
|
| 28 | +COMMIT;
|
Index: trunk/extensions/ConfirmAccount/ConfirmAccount_body.php |
— | — | @@ -0,0 +1,612 @@ |
| 2 | +<?php
|
| 3 | +
|
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) {
|
| 5 | + echo "ConfirmAccount extension\n";
|
| 6 | + exit( 1 );
|
| 7 | +}
|
| 8 | +
|
| 9 | +# Add messages
|
| 10 | +efLoadConfirmAccountsMessages();
|
| 11 | +
|
| 12 | +class RequestAccountPage extends SpecialPage {
|
| 13 | + function __construct() {
|
| 14 | + parent::__construct( 'RequestAccount' );
|
| 15 | + }
|
| 16 | +
|
| 17 | + function execute( $subpage ) {
|
| 18 | + global $wgUser, $wgOut, $wgRequest, $action ;
|
| 19 | +
|
| 20 | + if( $wgUser->isBlocked() ) {
|
| 21 | + $wgOut->blockedPage();
|
| 22 | + return;
|
| 23 | + }
|
| 24 | + if ( wfReadOnly() ) {
|
| 25 | + $wgOut->readOnlyPage();
|
| 26 | + return;
|
| 27 | + }
|
| 28 | +
|
| 29 | + $this->setHeaders();
|
| 30 | +
|
| 31 | + $this->mUsername = $wgRequest->getText( 'wpUsername' );
|
| 32 | + $this->mRealName = $wgRequest->getText( 'wpRealName' );
|
| 33 | + $this->mEmail = $wgRequest->getText( 'wpEmail' );
|
| 34 | + $this->mBio = $wgRequest->getText( 'wpBio' );
|
| 35 | + $this->mNotes = $wgRequest->getText( 'wpNotes' );
|
| 36 | + $this->mUrls = $wgRequest->getText( 'wpUrls' );
|
| 37 | + $emailCode = $wgRequest->getText( 'wpEmailToken' );
|
| 38 | +
|
| 39 | + if ( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
|
| 40 | + $this->doSubmit();
|
| 41 | + } else if( $action == 'confirmemail' ) {
|
| 42 | + $this->confirmEmailToken( $emailCode );
|
| 43 | + } else {
|
| 44 | + $this->showForm();
|
| 45 | + }
|
| 46 | + }
|
| 47 | +
|
| 48 | + function showForm( $msg='' ) {
|
| 49 | + global $wgOut, $wgUser, $wgTitle, $wgAuth;
|
| 50 | +
|
| 51 | + $wgOut->setPagetitle( wfMsg( "requestaccount" ) );
|
| 52 | + # Output failure message
|
| 53 | + if( $msg ) {
|
| 54 | + $wgOut->addHTML( '<div class="errorbox">' . $msg . '</div><div class="visualClear"></div>' );
|
| 55 | + }
|
| 56 | + # Give notice to users that are logged in
|
| 57 | + if( $wgUser->getID() ) {
|
| 58 | + $wgOut->addWikiText( wfMsg( "requestaccount-dup" ) );
|
| 59 | + }
|
| 60 | +
|
| 61 | + $wgOut->addWikiText( wfMsg( "requestacount-text" ) );
|
| 62 | +
|
| 63 | + $action = $wgTitle->escapeLocalUrl( 'action=submit' );
|
| 64 | + $form = "<form name='accountrequest' action='$action' method='post'><fieldset>";
|
| 65 | + $form .= '<legend>' . wfMsg('requestacount-legend1') . '</legend>';
|
| 66 | + $form .= "<p>".wfMsgExt( 'requestacount-acc-text', array('parse') )."</p>\n";
|
| 67 | + $form .= '<table cellpadding=\'4\'>';
|
| 68 | + $form .= "<tr><td>".Xml::label( wfMsgHtml('username'), 'wpUsername' )."</td>";
|
| 69 | + $form .= "<td>".Xml::input( 'wpUsername', 30, $this->mUsername, array('id' => 'wpUsername') )."</td></tr>\n";
|
| 70 | + $form .= "<tr><td>".Xml::label( wfMsgHtml('requestaccount-email'), 'wpEmail' )."</td>";
|
| 71 | + $form .= "<td>".Xml::input( 'wpEmail', 30, $this->mEmail, array('id' => 'wpEmail') )."</td></tr>\n";
|
| 72 | + $form .= '</table></fieldset>';
|
| 73 | +
|
| 74 | + $form .= '<fieldset>';
|
| 75 | + $form .= '<legend>' . wfMsg('requestacount-legend2') . '</legend>';
|
| 76 | + $form .= "<p>".wfMsgExt( 'requestaccount-bio-text', array('parse') )."</p>\n";
|
| 77 | + $form .= '<table cellpadding=\'4\'>';
|
| 78 | + $form .= "<tr><td>".Xml::label( wfMsgHtml('requestaccount-real'), 'wpRealName' )."</td>";
|
| 79 | + $form .= "<td>".Xml::input( 'wpRealName', 35, $this->mRealName, array('id' => 'wpRealName') )."</td></tr>\n";
|
| 80 | + $form .= '</table cellpadding=\'4\'>';
|
| 81 | + $form .= "<p>".wfMsgHtml('requestaccount-bio')."</p>";
|
| 82 | + $form .= "<p><textarea tabindex='1' name='wpBio' id='wpBio' rows='10' cols='80' style='width:100%'>" .
|
| 83 | + $this->mBio .
|
| 84 | + "</textarea></p>";
|
| 85 | + $form .= '</fieldset>';
|
| 86 | +
|
| 87 | + $form .= '<fieldset>';
|
| 88 | + $form .= '<legend>' . wfMsg('requestacount-legend3') . '</legend>';
|
| 89 | + $form .= "<p>".wfMsgExt( 'requestacount-ext-text', array('parse') )."</p>\n";
|
| 90 | +
|
| 91 | + $form .= "<p>".wfMsgHtml('requestaccount-notes')."</p>\n";
|
| 92 | + $form .= "<p><textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>" .
|
| 93 | + $this->mNotes .
|
| 94 | + "</textarea></p>";
|
| 95 | + $form .= "<p>".wfMsgHtml('requestaccount-urls')."</p>\n";
|
| 96 | + $form .= "<p><textarea tabindex='1' name='wpUrls' id='wpUrls' rows='2' cols='80' style='width:100%'>" .
|
| 97 | + $this->mUrls .
|
| 98 | + "</textarea></p>";
|
| 99 | +
|
| 100 | + $form .= Xml::hidden( 'title', $wgTitle->getPrefixedText() )."\n";
|
| 101 | + $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken() )."\n";
|
| 102 | + $form .= '</fieldset>';
|
| 103 | +
|
| 104 | + # Pseudo template for extensions
|
| 105 | + global $wgCaptcha;
|
| 106 | + if( isset($wgCaptcha) ) {
|
| 107 | + $template = new UsercreateTemplate();
|
| 108 | + $template->set( 'header', '' );
|
| 109 | + # Hook point to add captchas
|
| 110 | + $wgCaptcha->injectUserCreate( $template );
|
| 111 | + if( $template->data['header'] ) {
|
| 112 | + $form .= '<fieldset>';
|
| 113 | + $form .= $template->data['header'];
|
| 114 | + $form .= '</fieldset>';
|
| 115 | + }
|
| 116 | + }
|
| 117 | +
|
| 118 | + $form .= "<p>".wfMsgExt( 'requestacount-confirm', array('parse') )."</p>\n";
|
| 119 | + $form .= "<p>".Xml::submitButton( wfMsgHtml( 'requestacount-submit') ) . "</p></fieldset>";
|
| 120 | + $form .= '</form>';
|
| 121 | +
|
| 122 | + $wgOut->addHTML( $form );
|
| 123 | + }
|
| 124 | +
|
| 125 | + function doSubmit() {
|
| 126 | + global $wgOut, $wgUser, $wgAuth, $wgAccountRequestThrottle, $wgMemc;
|
| 127 | +
|
| 128 | + # Now create a dummy user ($u) and check if it is valid
|
| 129 | + $name = trim( $this->mUsername );
|
| 130 | + $u = User::newFromName( $name, 'creatable' );
|
| 131 | + if( is_null( $u ) ) {
|
| 132 | + $this->showForm( wfMsgHtml('noname') );
|
| 133 | + return;
|
| 134 | + }
|
| 135 | + # Check if already in use
|
| 136 | + if( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) {
|
| 137 | + $this->showForm( wfMsgHtml('userexists') );
|
| 138 | + return;
|
| 139 | + }
|
| 140 | + # Check pending accounts for name use
|
| 141 | + $dbw = wfGetDB( DB_MASTER );
|
| 142 | + $dup = $dbw->selectField( 'account_requests', '1',
|
| 143 | + array( 'acr_name' => $u->getName() ),
|
| 144 | + __METHOD__ );
|
| 145 | + if( $dup ) {
|
| 146 | + $this->showForm( wfMsgHtml('requestaccount-inuse') );
|
| 147 | + return;
|
| 148 | + }
|
| 149 | + # Validate email address
|
| 150 | + if( !$u->isValidEmailAddr( $this->mEmail ) ) {
|
| 151 | + $this->showForm( wfMsgHtml('invalidemailaddress') );
|
| 152 | + return;
|
| 153 | + }
|
| 154 | + # Set some additional data so the AbortNewAccount hook can be
|
| 155 | + # used for more than just username validation
|
| 156 | + $u->setEmail( $this->mEmail );
|
| 157 | + $u->setRealName( $this->mRealName );
|
| 158 | + # Let captchas confirm
|
| 159 | + global $wgCaptcha;
|
| 160 | + if( isset($wgCaptcha) ) {
|
| 161 | + $abortError = '';
|
| 162 | + $wgCaptcha->confirmUserCreate( $u, &$abortError );
|
| 163 | + if( $abortError ) {
|
| 164 | + $this->showForm( $abortError );
|
| 165 | + return false;
|
| 166 | + }
|
| 167 | + }
|
| 168 | + # Insert into pending requests...
|
| 169 | + $dbw->begin();
|
| 170 | + $dbw->insert( 'account_requests',
|
| 171 | + array(
|
| 172 | + 'acr_name' => $u->mName,
|
| 173 | + 'acr_email' => $u->mEmail,
|
| 174 | + 'acr_real_name' => $u->mRealName,
|
| 175 | + 'acr_registration' => wfTimestampNow(),
|
| 176 | + 'acr_bio' => $this->mBio,
|
| 177 | + 'acr_notes' => $this->mNotes,
|
| 178 | + 'acr_urls' => $this->mUrls,
|
| 179 | + 'acr_ip' => wfGetIP() // Possible use for spam blocking
|
| 180 | + ),
|
| 181 | + __METHOD__
|
| 182 | + );
|
| 183 | + # Send confirmation, required!
|
| 184 | + $result = $this->sendConfirmationMail( $u );
|
| 185 | + if( WikiError::isError( $result ) ) {
|
| 186 | + $error = wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) );
|
| 187 | + $this->showForm( $error );
|
| 188 | + $dbw->rollback(); // Nevermind
|
| 189 | + return false;
|
| 190 | + }
|
| 191 | + $dbw->commit();
|
| 192 | + # Now request spamming!
|
| 193 | + if( $wgAccountRequestThrottle && $wgUser->isPingLimitable() ) {
|
| 194 | + $key = wfMemcKey( 'acctrequest', 'ip', wfGetIP() );
|
| 195 | + $value = $wgMemc->incr( $key );
|
| 196 | + if( !$value ) {
|
| 197 | + $wgMemc->set( $key, 1, 86400 );
|
| 198 | + }
|
| 199 | + if( $value > $wgAccountRequestThrottle ) {
|
| 200 | + $this->throttleHit( $wgAccountRequestThrottle );
|
| 201 | + return false;
|
| 202 | + }
|
| 203 | + }
|
| 204 | + # Done!
|
| 205 | + $this->showSuccess();
|
| 206 | + }
|
| 207 | +
|
| 208 | + function showSuccess() {
|
| 209 | + global $wgOut;
|
| 210 | +
|
| 211 | + $wgOut->setPagetitle( wfMsg( "requestaccount" ) );
|
| 212 | + $wgOut->addWikiText( wfMsg( "requestaccount-sent" ) );
|
| 213 | +
|
| 214 | + $wgOut->returnToMain();
|
| 215 | + }
|
| 216 | +
|
| 217 | + /**
|
| 218 | + * @private
|
| 219 | + */
|
| 220 | + function throttleHit( $limit ) {
|
| 221 | + global $wgOut;
|
| 222 | +
|
| 223 | + $wgOut->addWikiText( wfMsg( 'acct_request_throttle_hit', $limit ) );
|
| 224 | + }
|
| 225 | +
|
| 226 | + function confirmEmailToken( $code ) {
|
| 227 | + global $wgUser, $wgOut;
|
| 228 | + # Confirm if this token is in the pending requests
|
| 229 | + $name = $this->requestFromEmailToken( $code );
|
| 230 | + if( $name !== false ) {
|
| 231 | + $this->confirmEmail( $name );
|
| 232 | + $message = $wgUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success';
|
| 233 | + $wgOut->addWikiText( wfMsg( $message ) );
|
| 234 | + $wgOut->returnToMain();
|
| 235 | + return;
|
| 236 | + }
|
| 237 | + # Maybe the user confirmed after account was created...
|
| 238 | + $user = User::newFromConfirmationCode( $code );
|
| 239 | + if( is_object( $user ) ) {
|
| 240 | + if( $user->confirmEmail() ) {
|
| 241 | + $message = $wgUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success';
|
| 242 | + $wgOut->addWikiText( wfMsg( $message ) );
|
| 243 | + if( !$wgUser->isLoggedIn() ) {
|
| 244 | + $title = SpecialPage::getTitleFor( 'Userlogin' );
|
| 245 | + $wgOut->returnToMain( true, $title->getPrefixedText() );
|
| 246 | + }
|
| 247 | + } else {
|
| 248 | + $wgOut->addWikiText( wfMsg( 'confirmemail_error' ) );
|
| 249 | + }
|
| 250 | + } else {
|
| 251 | + $wgOut->addWikiText( wfMsg( 'confirmemail_invalid' ) );
|
| 252 | + }
|
| 253 | + }
|
| 254 | +
|
| 255 | + /**
|
| 256 | + * Get a request ID from an emailconfirm token
|
| 257 | + *
|
| 258 | + * @param Sring $code
|
| 259 | + * @returns Integer $reqID
|
| 260 | + */
|
| 261 | + function requestFromEmailToken( $code ) {
|
| 262 | + $dbr = wfGetDB( DB_SLAVE );
|
| 263 | + $reqID = $dbr->selectField( 'account_requests', 'acr_name',
|
| 264 | + array( 'acr_email_token' => md5( $code ),
|
| 265 | + 'acr_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
|
| 266 | + )
|
| 267 | + );
|
| 268 | + return $reqID;
|
| 269 | + }
|
| 270 | +
|
| 271 | + /**
|
| 272 | + * Flag a user's email as confirmed in the db
|
| 273 | + *
|
| 274 | + * @param Sring $name
|
| 275 | + */
|
| 276 | + function confirmEmail( $name ) {
|
| 277 | + $dbw = wfGetDB( DB_MASTER );
|
| 278 | + $dbw->update( 'account_requests',
|
| 279 | + array( 'acr_email_authenticated' => wfTimestampNow() ),
|
| 280 | + array( 'acr_name' => $name ),
|
| 281 | + __METHOD__ );
|
| 282 | + }
|
| 283 | +
|
| 284 | + /**
|
| 285 | + * Generate a new e-mail confirmation token and send a confirmation
|
| 286 | + * mail to the user's given address.
|
| 287 | + *
|
| 288 | + * @param User $user
|
| 289 | + * @return mixed True on success, a WikiError object on failure.
|
| 290 | + */
|
| 291 | + function sendConfirmationMail( $user ) {
|
| 292 | + global $wgContLang;
|
| 293 | + $expiration = null; // gets passed-by-ref and defined in next line.
|
| 294 | + $url = $this->confirmationTokenUrl( $user, $expiration );
|
| 295 | + return $user->sendMail( wfMsg( 'requestaccount-email-subj' ),
|
| 296 | + wfMsg( 'requestaccount-email-body',
|
| 297 | + wfGetIP(),
|
| 298 | + $user->getName(),
|
| 299 | + $url,
|
| 300 | + $wgContLang->timeanddate( $expiration, false ) ) );
|
| 301 | + }
|
| 302 | +
|
| 303 | + /**
|
| 304 | + * Generate and store a new e-mail confirmation token, and return
|
| 305 | + * the URL the user can use to confirm.
|
| 306 | + * @param User $user
|
| 307 | + * @return string
|
| 308 | + * @private
|
| 309 | + */
|
| 310 | + function confirmationTokenUrl( $user, &$expiration ) {
|
| 311 | + $token = $this->confirmationToken( $user, $expiration );
|
| 312 | + $title = Title::makeTitle( NS_SPECIAL, 'RequestAccount' );
|
| 313 | + return $title->getFullUrl( 'action=confirmemail&wpEmailToken='.$token );
|
| 314 | + }
|
| 315 | +
|
| 316 | + /**
|
| 317 | + * Generate, store, and return a new e-mail confirmation code.
|
| 318 | + * A hash (unsalted since it's used as a key) is stored.
|
| 319 | + * @param User $user
|
| 320 | + * @return string
|
| 321 | + * @private
|
| 322 | + */
|
| 323 | + function confirmationToken( $user, &$expiration ) {
|
| 324 | + $now = time();
|
| 325 | + $expires = $now + 7 * 24 * 60 * 60;
|
| 326 | + $expiration = wfTimestamp( TS_MW, $expires );
|
| 327 | +
|
| 328 | + $token = $user->generateToken( $user->getName() . $user->getEmail() . $expires );
|
| 329 | + $hash = md5( $token );
|
| 330 | +
|
| 331 | + $dbw = wfGetDB( DB_MASTER );
|
| 332 | + $dbw->update( 'account_requests',
|
| 333 | + array( 'acr_email_token' => $hash,
|
| 334 | + 'acr_email_token_expires' => $dbw->timestamp( $expires ) ),
|
| 335 | + array( 'acr_name' => $user->getName() ),
|
| 336 | + __METHOD__ );
|
| 337 | +
|
| 338 | + return $token;
|
| 339 | + }
|
| 340 | +
|
| 341 | +}
|
| 342 | +
|
| 343 | +class ConfirmAccountsPage extends SpecialPage
|
| 344 | +{
|
| 345 | +
|
| 346 | + function __construct() {
|
| 347 | + SpecialPage::SpecialPage('ConfirmAccounts','confirmaccount');
|
| 348 | + }
|
| 349 | +
|
| 350 | + function execute( $par ) {
|
| 351 | + global $wgRequest, $wgOut, $wgUser;
|
| 352 | +
|
| 353 | + if( !$wgUser->isAllowed( 'confirmaccount' ) ) {
|
| 354 | + $wgOut->permissionRequired( 'confirmaccount' );
|
| 355 | + return;
|
| 356 | + }
|
| 357 | +
|
| 358 | + $this->setHeaders();
|
| 359 | + # A target user
|
| 360 | + $this->acrID = $wgRequest->getIntOrNull( 'acrid' );
|
| 361 | + # For renaming to alot for collisions with other local requests
|
| 362 | + # that were added to some global $wgAuth system first.
|
| 363 | + $this->mUsername = $wgRequest->getIntOrNull( 'wpNewName' );
|
| 364 | +
|
| 365 | + $this->skin = $wgUser->getSkin();
|
| 366 | +
|
| 367 | + if ( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
|
| 368 | + $this->doSubmit();
|
| 369 | + } else if( $this->acrID ) {
|
| 370 | + $this->showForm();
|
| 371 | + } else {
|
| 372 | + $this->showList();
|
| 373 | + }
|
| 374 | + }
|
| 375 | +
|
| 376 | + function doSubmit() {
|
| 377 | + global $wgOut, $wgTitle, $wgAuth, $action;
|
| 378 | +
|
| 379 | + $row = $this->getRequest();
|
| 380 | + if( !$row ) {
|
| 381 | + $wgOut->addHTML( wfMsg('confirmaccount-badid') );
|
| 382 | + $wgOut->returnToMain( null, $wgTitle );
|
| 383 | + return;
|
| 384 | + }
|
| 385 | +
|
| 386 | + if( $action == 'reject' ) {
|
| 387 | + $dbw = wfGetDB( DB_MASTER );
|
| 388 | + $dbw->delete( 'account_requests', array('acr_id' => $this->acrID), __METHOD__ );
|
| 389 | +
|
| 390 | + $this->showSuccess( $action );
|
| 391 | + } else if( $action == 'accept' ) {
|
| 392 | + global $wgMakeUserPageFromBio;
|
| 393 | + # Check if the name is to be overridden
|
| 394 | + $name = $this->mUsername ? trim($this->mUsername) : $row->acr_name;
|
| 395 | + # Now create a dummy user ($u) and check if it is valid
|
| 396 | + $u = User::newFromName( $name, 'creatable' );
|
| 397 | + if( is_null( $u ) ) {
|
| 398 | + $this->showForm( wfMsgHtml('noname') );
|
| 399 | + return;
|
| 400 | + }
|
| 401 | + # Check if already in use
|
| 402 | + if( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) {
|
| 403 | + $this->showForm( wfMsgHtml('userexists') );
|
| 404 | + return;
|
| 405 | + }
|
| 406 | +
|
| 407 | + $pass = User::randomPassword();
|
| 408 | + if( !$wgAuth->addUser( $u, $pass, $row->acr_email, $row->acr_real_name ) ) {
|
| 409 | + $this->showForm( wfMsg( 'externaldberror' ) );
|
| 410 | + return false;
|
| 411 | + }
|
| 412 | + # Now that name is validated, create the stub account
|
| 413 | + $user = User::createNew( $name );
|
| 414 | + # VERY important to set email now. Otherwise user will have to request
|
| 415 | + # a new password at the login screen...
|
| 416 | + $user->setEmail( $row->acr_email );
|
| 417 | + $user->setRealName( $row->acr_real_name );
|
| 418 | + $user->setPassword( $pass );
|
| 419 | + $user->saveSettings(); // Save this stuff now
|
| 420 | + # Email this password
|
| 421 | + $user->sendMail( wfMsg( 'confirmaccount-email-subj' ),
|
| 422 | + wfMsg( 'confirmaccount-email-body',
|
| 423 | + $user->getName(),
|
| 424 | + $pass ) );
|
| 425 | + # Check if the user already confirmed email address
|
| 426 | + $dbw = wfGetDB( DB_MASTER );
|
| 427 | + $dbw->update( 'user',
|
| 428 | + array( 'user_email_authenticated' => $row->acr_email_authenticated ),
|
| 429 | + array( 'user_id' => $user->getID() ),
|
| 430 | + __METHOD__ );
|
| 431 | +
|
| 432 | + # OK, now remove the request
|
| 433 | + $dbw->delete( 'account_requests', array('acr_id' => $this->acrID), __METHOD__ );
|
| 434 | +
|
| 435 | + wfRunHooks( 'AddNewAccount', array( $user ) );
|
| 436 | + # Start up the user's brand new userpage
|
| 437 | + if( $wgMakeUserPageFromBio ) {
|
| 438 | + $userpage = new Article( $user->getUserPage() );
|
| 439 | + $userpage->doEdit( $row->acr_bio, wfMsg('confirmaccount-summary'), EDIT_NEW );
|
| 440 | + }
|
| 441 | +
|
| 442 | + $this->showSuccess( $action, $user->getName() );
|
| 443 | + }
|
| 444 | + }
|
| 445 | +
|
| 446 | + function showForm( $msg='' ) {
|
| 447 | + global $wgOut, $wgTitle, $wgUser;
|
| 448 | +
|
| 449 | + # Output failure message
|
| 450 | + if( $msg ) {
|
| 451 | + $wgOut->addHTML( '<div class="errorbox">' . $msg . '</div><div class="visualClear"></div>' );
|
| 452 | + }
|
| 453 | +
|
| 454 | + $listLink = $this->skin->makeKnownLinkObj( $wgTitle, wfMsgHtml( 'confirmaccount-back' ) );
|
| 455 | + $wgOut->setSubtitle( '<p>'.$listLink.'</p>' );
|
| 456 | +
|
| 457 | + $row = $this->getRequest();
|
| 458 | + if( !$row ) {
|
| 459 | + $wgOut->addHTML( wfMsg('confirmaccount-badid') );
|
| 460 | + $wgOut->returnToMain( null, $wgTitle );
|
| 461 | + return;
|
| 462 | + }
|
| 463 | +
|
| 464 | + $wgOut->addWikiText( wfMsg( "confirmacount-text" ) );
|
| 465 | +
|
| 466 | + $action = $wgTitle->escapeLocalUrl( 'action=submit' );
|
| 467 | + $form = "<form name='accountconfirm' action='$action' method='post'><fieldset>";
|
| 468 | + $form .= '<legend>' . wfMsg('requestacount-legend1') . '</legend>';
|
| 469 | + $form .= '<table cellpadding=\'4\'>';
|
| 470 | + $form .= "<tr><td>".Xml::label( wfMsgHtml('username'), 'wpUsername' )."</td>";
|
| 471 | + $form .= "<td>".Xml::input( 'wpUsername', 30, $row->acr_name, array('id' => 'wpUsername') )."</td></tr>\n";
|
| 472 | +
|
| 473 | + $econf = $row->acr_email_authenticated ? ' <strong>'.wfMsg('confirmaccount-econf').'</strong>' : '';
|
| 474 | + $form .= "<tr><td>".wfMsgHtml('requestaccount-email')."</td>";
|
| 475 | + $form .= "<td>".$row->acr_email.$econf."</td></tr>\n";
|
| 476 | + $form .= '</table></fieldset>';
|
| 477 | +
|
| 478 | + $form .= '<fieldset>';
|
| 479 | + $form .= '<legend>' . wfMsg('requestacount-legend2') . '</legend>';
|
| 480 | + $form .= '<table cellpadding=\'4\'>';
|
| 481 | + $form .= "<tr><td>".wfMsgHtml('requestaccount-real')."</td>";
|
| 482 | + $form .= "<td>".$row->acr_real_name."</td></tr>\n";
|
| 483 | + $form .= '</table cellpadding=\'4\'>';
|
| 484 | + $form .= "<p>".wfMsgHtml('requestaccount-bio')."</p>";
|
| 485 | + $form .= "<p><textarea tabindex='1' readonly name='wpBio' id='wpBio' rows='10' cols='80' style='width:100%'>" .
|
| 486 | + $row->acr_bio .
|
| 487 | + "</textarea></p>";
|
| 488 | + $form .= '</fieldset>';
|
| 489 | +
|
| 490 | + $form .= '<fieldset>';
|
| 491 | + $form .= '<legend>' . wfMsg('requestacount-legend3') . '</legend>';
|
| 492 | + $form .= "<p>".wfMsgHtml('requestaccount-notes')."</p>\n";
|
| 493 | + $form .= "<p><textarea tabindex='1' readonly name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>" .
|
| 494 | + $row->acr_notes .
|
| 495 | + "</textarea></p>";
|
| 496 | + $form .= "<p>".wfMsgHtml('requestaccount-urls')."</p>\n";
|
| 497 | + $form .= "<p><textarea tabindex='1' readonly name='wpUrls' id='wpUrls' rows='2' cols='80' style='width:100%'>" .
|
| 498 | + $row->acr_urls .
|
| 499 | + "</textarea></p>";
|
| 500 | + $form .= '</fieldset>';
|
| 501 | +
|
| 502 | + $form .= Xml::hidden( 'title', $wgTitle->getPrefixedText() )."\n";
|
| 503 | + $form .= Xml::hidden( 'action', 'accept' );
|
| 504 | + $form .= Xml::hidden( 'acrid', $row->acr_id );
|
| 505 | + $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken() )."\n";
|
| 506 | +
|
| 507 | + $form .= "<p>".wfMsgExt( 'confirmacount-confirm', array('parse') )."</p>\n";
|
| 508 | + $form .= '<div style="float: left">'.Xml::submitButton( wfMsgHtml( 'confirmacount-create') ).'</div>';
|
| 509 | + $form .= '</form>';
|
| 510 | + # Make deny use a separate form to avoid problems with people pressing enter
|
| 511 | + $form .= "<form name='accountreject' action='$action' method='post'>";
|
| 512 | + $form .= '<div style="float: right">'.Xml::submitButton( wfMsgHtml( 'confirmacount-deny') ) . "</div>";
|
| 513 | + $form .= Xml::hidden( 'title', $wgTitle->getPrefixedText() )."\n";
|
| 514 | + $form .= Xml::hidden( 'action', 'reject' );
|
| 515 | + $form .= Xml::hidden( 'acrid', $row->acr_id );
|
| 516 | + $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken() )."\n";
|
| 517 | + $form .= '</form>';
|
| 518 | +
|
| 519 | + $wgOut->addHTML( $form );
|
| 520 | + }
|
| 521 | +
|
| 522 | + function getRequest() {
|
| 523 | + if( !$this->acrID )
|
| 524 | + return false;
|
| 525 | +
|
| 526 | + $dbw = wfGetDB( DB_SLAVE );
|
| 527 | + $row = $dbw->selectRow( 'account_requests', '*', array('acr_id' => $this->acrID ), __METHOD__ );
|
| 528 | +
|
| 529 | + return $row;
|
| 530 | + }
|
| 531 | +
|
| 532 | + function showSuccess( $action, $name = NULL ) {
|
| 533 | + global $wgOut, $wgTitle;
|
| 534 | +
|
| 535 | + $wgOut->setPagetitle( wfMsg( "requestaccount" ) );
|
| 536 | + if( $action == 'accept' )
|
| 537 | + $wgOut->addWikiText( wfMsg( "confirmaccount-acc", $name ) );
|
| 538 | + else
|
| 539 | + $wgOut->addWikiText( wfMsg( "confirmaccount-del" ) );
|
| 540 | +
|
| 541 | + $wgOut->returnToMain( null, $wgTitle );
|
| 542 | + }
|
| 543 | +
|
| 544 | + function showList() {
|
| 545 | + global $wgOut, $wgUser, $wgLang;
|
| 546 | +
|
| 547 | + $pager = new ConfirmAccountsPager( $this, array() );
|
| 548 | + if ( $pager->getNumRows() ) {
|
| 549 | + $wgOut->addHTML( wfMsgExt('confirmacount-list', array('parse') ) );
|
| 550 | + $wgOut->addHTML( $pager->getNavigationBar() );
|
| 551 | + $wgOut->addHTML( "<ul>" . $pager->getBody() . "</ul>" );
|
| 552 | + $wgOut->addHTML( $pager->getNavigationBar() );
|
| 553 | + } else {
|
| 554 | + $wgOut->addHTML( wfMsgExt('confirmacount-none', array('parse')) );
|
| 555 | + }
|
| 556 | + }
|
| 557 | +
|
| 558 | + function formatRow( $row ) {
|
| 559 | + global $wgLang, $wgUser;
|
| 560 | +
|
| 561 | + $title = SpecialPage::getTitleFor( 'ConfirmAccounts' );
|
| 562 | + $link = $this->skin->makeKnownLinkObj( $title, wfMsg('confirmaccount-review'), 'acrid='.$row->acr_id );
|
| 563 | + $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->acr_registration), true );
|
| 564 | +
|
| 565 | + $r = '<li>';
|
| 566 | + $r .= $time." ($link)".'<br/>';
|
| 567 | + $r .= '<table cellspacing=\'1\' cellpadding=\'3\' border=\'1\' width=\'100%\'>';
|
| 568 | + $r .= '<tr><td><strong>'.wfMsg('confirmaccount-name').'</strong></td><td width=\'100%\'>'.$row->acr_name.'</td></tr>';
|
| 569 | + $r .= '<tr><td><strong>'.wfMsg('confirmaccount-real').'</strong></td><td width=\'100%\'>'.$row->acr_real_name.'</td></tr>';
|
| 570 | + $econf = $row->acr_email_authenticated ? ' <strong>'.wfMsg('confirmaccount-econf').'</strong>' : '';
|
| 571 | + $r .= '<tr><td><strong>'.wfMsg('confirmaccount-email').'</strong></td><td width=\'100%\'>'.$row->acr_email.$econf.'</td></tr>';
|
| 572 | + # Truncate this, blah blah...
|
| 573 | + $bio = substr( $row->acr_bio, 0, 500 );
|
| 574 | + $bio = strlen($bio) < strlen($row->acr_bio) ? "$bio . . ." : $bio;
|
| 575 | +
|
| 576 | + $r .= '<tr><td><strong>'.wfMsg('confirmaccount-bio').'</strong></td><td width=\'100%\'><i>'.$bio.'</i></td></tr>';
|
| 577 | + $r .= '</table></li>';
|
| 578 | +
|
| 579 | + return $r;
|
| 580 | + }
|
| 581 | +}
|
| 582 | +
|
| 583 | +/**
|
| 584 | + * Query to list out stable versions for a page
|
| 585 | + */
|
| 586 | +class ConfirmAccountsPager extends ReverseChronologicalPager {
|
| 587 | + public $mForm, $mConds;
|
| 588 | +
|
| 589 | + function __construct( $form, $conds = array() ) {
|
| 590 | + $this->mForm = $form;
|
| 591 | + $this->mConds = $conds;
|
| 592 | + parent::__construct();
|
| 593 | + }
|
| 594 | +
|
| 595 | + function formatRow( $row ) {
|
| 596 | + $block = new Block;
|
| 597 | + return $this->mForm->formatRow( $row );
|
| 598 | + }
|
| 599 | +
|
| 600 | + function getQueryInfo() {
|
| 601 | + $conds = $this->mConds;
|
| 602 | + return array(
|
| 603 | + 'tables' => array('account_requests'),
|
| 604 | + 'fields' => 'acr_id,acr_name,acr_real_name,acr_registration,acr_email,acr_email_authenticated,
|
| 605 | + acr_bio,acr_notes,acr_urls',
|
| 606 | + 'conds' => $conds
|
| 607 | + );
|
| 608 | + }
|
| 609 | +
|
| 610 | + function getIndexField() {
|
| 611 | + return 'acr_registration';
|
| 612 | + }
|
| 613 | +}
|
Index: trunk/extensions/ConfirmAccount/ConfirmAccount.i18n.php |
— | — | @@ -0,0 +1,88 @@ |
| 2 | +<?php
|
| 3 | +/**
|
| 4 | + * Internationalisation file for ConfirmAccount extension.
|
| 5 | + *
|
| 6 | + * @addtogroup Extensions
|
| 7 | +*/
|
| 8 | +
|
| 9 | +$wgConfirmAccountMessages = array();
|
| 10 | +
|
| 11 | +$wgConfirmAccountMessages['en'] = array(
|
| 12 | + # Request account page
|
| 13 | + 'requestaccount' => 'Request account',
|
| 14 | + 'requestacount-text' => '\'\'\'Complete and submit the following form to request a user account\'\'\'.
|
| 15 | +
|
| 16 | + Your email address will be sent a confirmation message once this request is submited. Please respond by clicking
|
| 17 | + on the confirmation link provided by the the email.
|
| 18 | +
|
| 19 | + Once the account is approved, you will be emailed a notification message and the account will be usable at
|
| 20 | + [[Special:Userlogin]].',
|
| 21 | + 'requestaccount-dup' => '\'\'\'Note: You already are logged in with a registered account.\'\'\'',
|
| 22 | + 'requestacount-legend1' => 'User account:',
|
| 23 | + 'requestacount-legend2' => 'Personal information:',
|
| 24 | + 'requestacount-legend3' => 'Other information:',
|
| 25 | + 'requestacount-acc-text' => 'Your password will be emailed to you when your account is confirmed.',
|
| 26 | + 'requestacount-ext-text' => 'The following information is kept private and will only be used for this request.
|
| 27 | + You may want to list contacts such as fax/phone numbers to aid in identify confirmation.',
|
| 28 | + 'requestaccount-bio-text' => "Your biography will be set as the default content for your userpage. Try to include
|
| 29 | + any credentials. Make sure you are comfortable publishing such information. Your name can be changed via [[Special:Preferences]].",
|
| 30 | + 'requestaccount-real' => 'Real name:',
|
| 31 | + 'requestaccount-email' => 'Email address:',
|
| 32 | + 'requestaccount-bio' => 'Personal biography:',
|
| 33 | + 'requestaccount-notes' => 'Additional notes:',
|
| 34 | + 'requestaccount-urls' => 'Websites:',
|
| 35 | + 'requestaccount-inuse' => 'Username is already in use in a pending account request.',
|
| 36 | + 'requestacount-confirm' => 'Press the submit button below once you have confirmed that all the above is correct.',
|
| 37 | + 'requestacount-submit' => 'Request account',
|
| 38 | + 'requestaccount-sent' => 'Your account request has successfully been sent and is now pending review.',
|
| 39 | + 'requestaccount-email-subj' => '{{SITENAME}} e-mail address confirmation',
|
| 40 | + 'requestaccount-email-body' => 'Someone, probably you from IP address $1, has requested an
|
| 41 | +account "$2" with this e-mail address on {{SITENAME}}.
|
| 42 | +
|
| 43 | +To confirm that this account really does belong to you on {{SITENAME}}, open this link in your browser:
|
| 44 | +
|
| 45 | +$3
|
| 46 | +
|
| 47 | +If the account is created, only you will be emailed the password. If this is *not* you, don\'t follow the link.
|
| 48 | +This confirmation code will expire at $4.',
|
| 49 | +
|
| 50 | + 'acct_request_throttle_hit' => "Sorry, you have already requested $1 accounts. You can't make any more requests.",
|
| 51 | +
|
| 52 | + # Add to Special:Login
|
| 53 | + 'requestacount-loginnotice' => 'To obtain a user account, you must \'\'\'[[Special:RequestAccount|request one]].\'\'\'',
|
| 54 | +
|
| 55 | + # Confirm account page
|
| 56 | + 'confirmaccounts' => 'Confirm account requests',
|
| 57 | + 'confirmacount-list' => 'Below is a list of account requests awaiting approval.
|
| 58 | + Approved accounts will be created and removed from this list. Rejected accounts will simply be deleted from this
|
| 59 | + list.',
|
| 60 | + 'confirmacount-text' => 'This is a pending request for a user account at \'\'\'{{SITENAME}}\'\'\'. Carefully
|
| 61 | + review and if needed, confirm, all the below information. Note that you can choose to create the account under a
|
| 62 | + different username. Use this only to avoid collisions with other names.
|
| 63 | +
|
| 64 | + If you simply leave this page without confirming or denying this request, it will remain pending.',
|
| 65 | + 'confirmacount-none' => 'There are currently no pending account requests.',
|
| 66 | + 'confirmaccount-badid' => 'There is no pending request corresponding to the given ID. It may have already been handled.',
|
| 67 | + 'confirmaccount-back' => 'View pending account list',
|
| 68 | + 'confirmaccount-name' => 'Username',
|
| 69 | + 'confirmaccount-real' => 'Name',
|
| 70 | + 'confirmaccount-email' => 'Email',
|
| 71 | + 'confirmaccount-bio' => 'Biography',
|
| 72 | + 'confirmaccount-review' => 'Review this request in detail',
|
| 73 | + 'confirmacount-confirm' => 'Use the buttons below to irreversibly confirm this request and create the account or deny it.',
|
| 74 | + 'confirmaccount-econf' => '(confirmed)',
|
| 75 | + 'confirmacount-create' => 'Confirm (create account)',
|
| 76 | + 'confirmacount-deny' => 'Reject',
|
| 77 | + 'confirmaccount-acc' => 'Account request confirmed successfully; created new user account [[User:$1]].',
|
| 78 | + 'confirmaccount-del' => 'Account request rejected and successfully deleted.',
|
| 79 | + 'confirmaccount-summary' => 'Creating user page with biography of new user.',
|
| 80 | + 'confirmaccount-email-subj' => '{{SITENAME}} account request',
|
| 81 | + 'confirmaccount-email-body' => 'Your request for an account has been approved on {{SITENAME}}.
|
| 82 | +
|
| 83 | +Account name: $1
|
| 84 | +
|
| 85 | +Password: $2
|
| 86 | +
|
| 87 | +You may have been granted a slightly different name than requested. This could be due to name collisions
|
| 88 | +or policy reasons. Also, please immediatly login, go to your preferences options, and set a new password.',
|
| 89 | +);
|