changed password tool so that user specifies their password at account request

This commit is contained in:
jvvg 2013-06-13 10:57:54 -04:00
parent 4bf8b4e65f
commit 5b37e7dd88
5 changed files with 1170 additions and 1154 deletions

View file

@ -1,6 +1,5 @@
Complete online documenation:
Original version: http://www.mediawiki.org/wiki/Extension:ConfirmAccount
Modified version: http://wiki.scratch.mit.edu/wiki/User:Jvvg/Authentication
http://www.mediawiki.org/wiki/Extension:ConfirmAccount
== Breaking changes ==
=== MediaWiki 1.20 ===
@ -8,9 +7,5 @@ $wgAccountRequestMinWords, $wgAccountRequestToS, $wgAccountRequestExtraInfo,
and $wgAllowAccountRequestFiles were all folded into a new variable called
$wgConfirmAccountRequestFormItems.
=== Modified version ===
Gutted all email functionality - the user is given their temporary password when they register rather than when their request is accepted, and added a tool to verify registrations against the Scratch website.
== Licensing ==
Original version © GPL, Aaron Schulz
Modified version also available under GPL, by Jacob G.
© GPL, Aaron Schulz

View file

@ -1,450 +1,451 @@
<?php
class AccountConfirmSubmission {
/* User making the confirmation */
protected $admin;
/** @var UserAccountRequest */
protected $accReq;
/* Admin-overridable name and fields filled from request form */
protected $userName;
protected $bio;
protected $type;
/** @var array */
protected $areas;
protected $action;
protected $reason;
public function __construct( User $admin, UserAccountRequest $accReq, array $params ) {
$this->admin = $admin;
$this->accountReq = $accReq;
$this->userName = trim( $params['userName'] );
$this->bio = trim( $params['bio'] );
$this->type = $params['type'];
$this->areas = $params['areas'];
$this->action = $params['action'];
$this->reason = $params['reason'];
}
/**
* Attempt to validate and submit this data to the DB
* @param $context IContextSource
* @return array( true or error key string, html error msg or null )
*/
public function submit( IContextSource $context ) {
# Make sure that basic permissions are checked
if ( !$this->admin->getID() || !$this->admin->isAllowed( 'confirmaccount' ) ) {
return array( 'accountconf_permission_denied', $context->msg( 'badaccess-group0' )->escaped() );
} elseif ( wfReadOnly() ) {
return array( 'accountconf_readonly', $context->msg( 'badaccess-group0' )->escaped() );
}
if ( $this->action === 'spam' ) {
return $this->spamRequest( $context );
} elseif ( $this->action === 'reject' ) {
return $this->rejectRequest( $context );
} elseif ( $this->action === 'hold' ) {
return $this->holdRequest( $context );
} elseif ( $this->action === 'accept' ) {
return $this->acceptRequest( $context );
} else {
return array( 'accountconf_bad_action', $context->msg( 'confirmaccount-badaction' )->escaped() );
}
}
protected function spamRequest( IContextSource $context ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $this->accountReq->markRejected( $this->admin, wfTimestampNow(), '' );
if ( $ok ) {
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
}
$dbw->commit();
return array( true, null );
}
protected function rejectRequest( IContextSource $context ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $this->accountReq->markRejected( $this->admin, wfTimestampNow(), $this->reason );
if ( $ok ) {
/*
# Make proxy user to email a rejection message :(
$u = User::newFromName( $this->accountReq->getName(), false );
$u->setEmail( $this->accountReq->getEmail() );
# Send out a rejection email...
if ( $this->reason != '' ) {
$emailBody = $context->msg( 'confirmaccount-email-body4',
$u->getName(), $this->reason )->inContentLanguage()->text();
} else {
$emailBody = $context->msg( 'confirmaccount-email-body3',
$u->getName() )->inContentLanguage()->text();
}
$result = $u->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$emailBody
);
if ( !$result->isOk() ) {
$dbw->rollback();
return array( 'accountconf_mailerror',
$context->msg( 'mailerror' )->rawParams( $context->getOutput()->parse( $result->getWikiText() ) )->text() );
}
# Clear cache for notice of how many account requests there are
*/
ConfirmAccount::clearAccountRequestCountCache();
}
$dbw->commit();
return array( true, null );
}
protected function holdRequest( IContextSource $context ) {
# Make proxy user to email a message
$u = User::newFromName( $this->accountReq->getName(), false );
$u->setEmail( $this->accountReq->getEmail() );
# Pointless without a summary...
if ( $this->reason == '' ) {
return array( 'accountconf_needreason', $context->msg( 'confirmaccount-needreason' )->escaped() );
}
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
# If not already held or deleted, mark as held
$ok = $this->accountReq->markHeld( $this->admin, wfTimestampNow(), $this->reason );
if ( !$ok ) { // already held or deleted?
$dbw->rollback();
return array( 'accountconf_canthold', $context->msg( 'confirmaccount-canthold' )->escaped() );
}
# Send out a request hold email...
$result = $u->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$context->msg( 'confirmaccount-email-body5', $u->getName(), $this->reason )->inContentLanguage()->text()
);
if ( !$result->isOk() ) {
$dbw->rollback();
return array( 'accountconf_mailerror',
$context->msg( 'mailerror' )->rawParams( $context->getOutput()->parse( $result->getWikiText() ) )->text() );
}
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
$dbw->commit();
return array( true, null );
}
protected function acceptRequest( IContextSource $context ) {
global $wgAuth, $wgAccountRequestTypes, $wgConfirmAccountSaveInfo;
global $wgConfirmAccountRequestFormItems, $wgConfirmAccountFSRepos;
$formConfig = $wgConfirmAccountRequestFormItems; // convience
$accReq = $this->accountReq; // convenience
# Now create user and check if the name is valid
$user = User::newFromName( $this->userName, 'creatable' );
if ( !$user ) {
return array( 'accountconf_invalid_name', $context->msg( 'noname' )->escaped() );
}
# Check if account name is already in use
if ( 0 != $user->idForName() || $wgAuth->userExists( $user->getName() ) ) {
return array( 'accountconf_user_exists', $context->msg( 'userexists' )->escaped() );
}
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
# Make a random password
$p = md5(strtolower($accReq->getNotes()));
# Insert the new user into the DB...
$tokenExpires = $accReq->getEmailTokenExpires();
$authenticated = $accReq->getEmailAuthTimestamp();
$params = array(
# Set the user's real name
'real_name' => $accReq->getRealName(),
# Set the temporary password
'newpassword' => User::crypt( $p ),
# VERY important to set email now. Otherwise the user
# will have to request a new password at the login screen...
'email' => $accReq->getEmail(),
# Import email address confirmation status
'email_authenticated' => $dbw->timestampOrNull( $authenticated ),
'email_token_expires' => $dbw->timestamp( $tokenExpires ),
'email_token' => $accReq->getEmailToken()
);
$user = User::createNew( $user->getName(), $params );
# Grant any necessary rights (exclude blank or dummy groups)
$group = self::getGroupFromType( $this->type );
if ( $group != '' && $group != 'user' && $group != '*' ) {
$user->addGroup( $group );
}
$acd_id = null; // used for rollback cleanup
# Save account request data to credentials system
if ( $wgConfirmAccountSaveInfo ) {
$key = $accReq->getFileStorageKey();
# Copy any attached files to new storage group
if ( $formConfig['CV']['enabled'] && $key ) {
$repoOld = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$repoNew = new FSRepo( $wgConfirmAccountFSRepos['accountcreds'] );
$pathRel = UserAccountRequest::relPathFromKey( $key );
$oldPath = $repoOld->getZonePath( 'public' ) . '/' . $pathRel;
$triplet = array( $oldPath, 'public', $pathRel );
$status = $repoNew->storeBatch( array( $triplet ) ); // copy!
if ( !$status->isOK() ) {
$dbw->rollback();
# DELETE new rows in case there was a COMMIT somewhere
$this->acceptRequest_rollback( $dbw, $user->getId(), $acd_id );
return array( 'accountconf_copyfailed',
$context->getOutput()->parse( $status->getWikiText() ) );
}
}
$acd_id = $dbw->nextSequenceValue( 'account_credentials_acd_id_seq' );
# Move request data into a separate table
$dbw->insert( 'account_credentials',
array(
'acd_user_id' => $user->getID(),
'acd_real_name' => $accReq->getRealName(),
'acd_email' => $accReq->getEmail(),
'acd_email_authenticated' => $dbw->timestampOrNull( $authenticated ),
'acd_bio' => $accReq->getBio(),
'acd_notes' => $accReq->getNotes(),
'acd_urls' => $accReq->getUrls(),
'acd_ip' => $accReq->getIP(),
'acd_xff' => $accReq->getXFF(),
'acd_agent' => $accReq->getAgent(),
'acd_filename' => $accReq->getFileName(),
'acd_storage_key' => $accReq->getFileStorageKey(),
'acd_areas' => $accReq->getAreas( 'flat' ),
'acd_registration' => $dbw->timestamp( $accReq->getRegistration() ),
'acd_accepted' => $dbw->timestamp(),
'acd_user' => $this->admin->getID(),
'acd_comment' => $this->reason,
'acd_id' => $acd_id
),
__METHOD__
);
if ( is_null( $acd_id ) ) {
$acd_id = $dbw->insertId(); // set $acd_id to ID inserted
}
}
# Add to global user login system (if there is one)
if ( !$wgAuth->addUser( $user, $p, $accReq->getEmail(), $accReq->getRealName() ) ) {
$dbw->rollback();
# DELETE new rows in case there was a COMMIT somewhere
$this->acceptRequest_rollback( $dbw, $user->getId(), $acd_id );
return array( 'accountconf_externaldberror', $context->msg( 'externaldberror' )->escaped() );
}
# OK, now remove the request from the queue
$accReq->remove();
# Commit this if we make past the CentralAuth system
# and the groups are added. Next step is sending out an
# email, which we cannot take back...
$dbw->commit();
# Prepare a temporary password email...
if ( $this->reason != '' ) {
$msg = "confirmaccount-email-body2-pos{$this->type}";
# If the user is in a group and there is a welcome for that group, use it
if ( $group && !wfEmptyMsg( $msg ) ) {
$ebody = $context->msg( $msg, $user->getName(), $p, $this->reason )->inContentLanguage()->text();
# Use standard if none found...
} else {
$ebody = $context->msg( 'confirmaccount-email-body2',
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
}
} else {
$msg = "confirmaccount-email-body-pos{$this->type}";
# If the user is in a group and there is a welcome for that group, use it
if ( $group && !$context->msg( $msg )->isDisabled() ) {
$ebody = $context->msg( $msg,
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
# Use standard if none found...
} else {
$ebody = $context->msg( 'confirmaccount-email-body',
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
}
}
# Actually send out the email (@TODO: rollback on failure including $wgAuth)
$result = $user->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$ebody
);
# Update user count
$ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
$ssUpdate->doUpdate();
# Safe to hook/log now...
wfRunHooks( 'AddNewAccount', array( $user, false /* not by email */ ) );
$user->addNewUserLogEntry();
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
# Delete any attached file and don't stop the whole process if this fails
if ( $formConfig['CV']['enabled'] ) {
$key = $accReq->getFileStorageKey();
if ( $key ) {
$repoOld = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$pathRel = UserAccountRequest::relPathFromKey( $key );
$oldPath = $repoOld->getZonePath( 'public' ) . '/' . $pathRel;
if ( file_exists( $oldPath ) ) {
unlink( $oldPath ); // delete!
}
}
}
# Start up the user's userpages if set to do so.
# Will not append, so previous content will be blanked.
$this->createUserPage( $user );
# Greet the new user if set to do so.
$this->createUserTalkPage( $user );
return array( true, null );
}
/*
* Rollback an account acceptance *before* the request row and attachment are deleted.
* This is mostly here for sanity in case of COMMITs triggered elsewhere.
* http://bugs.mysql.com/bug.php?id=30767 behavoir assumed.
* @param $dbw Database
* @param $user_id int
* @param $acd_id int
* @return void
*/
protected function acceptRequest_rollback( DatabaseBase $dbw, $user_id, $acd_id ) {
$dbw->begin();
# DELETE the user in case something caused a COMMIT already somewhere.
if ( $user_id ) {
$dbw->delete( 'user', array( 'user_id' => $user_id ), __METHOD__ );
$dbw->delete( 'user_groups', array( 'ug_user' => $user_id ), __METHOD__ );
}
# DELETE the new account_credentials row likewise.
if ( $acd_id ) {
$dbw->delete( 'account_credentials', array( 'acd_id' => $acd_id ), __METHOD__ );
}
$dbw->commit();
}
protected static function getGroupFromType( $type ) {
global $wgAccountRequestTypes;
$group = '';
// Format is (type => (subpage par, group key, group text))
if ( isset( $wgAccountRequestTypes[$type][1] ) ) {
$group = $wgAccountRequestTypes[$type][1];
}
return $group;
}
protected static function getAutoTextFromType( $type ) {
global $wgAccountRequestTypes;
$groupText = '';
// Format is (type => (subpage par, group key, group text))
if ( isset( $wgAccountRequestTypes[$type][2] ) ) {
$groupText = $wgAccountRequestTypes[$type][2];
}
return $groupText;
}
protected function createUserPage( User $user ) {
global $wgMakeUserPageFromBio, $wgAutoUserBioText;
global $wgConfirmAccountSortkey, $wgContLang;
$body = ''; // page text
if ( $wgMakeUserPageFromBio ) {
# Add account request bio to userpage
$body .= $this->bio;
# Add any automatic text for all confirmed accounts
if ( $wgAutoUserBioText != '' ) {
$body .= "\n\n{$wgAutoUserBioText}";
}
}
# Add any automatic text for confirmed accounts of this type
$autoText = self::getAutoTextFromType( $this->type );
if ( $autoText != '' ) {
$body .= "\n\n{$autoText}";
}
# Add any areas of interest categories...
foreach ( ConfirmAccount::getUserAreaConfig() as $name => $conf ) {
if ( in_array( $name, $this->areas ) ) {
# General userpage text for anyone with this interest
if ( $conf['userText'] != '' ) {
$body .= $conf['userText'];
}
# Message for users with this interested with the given account type
if ( isset( $conf['grpUserText'][$this->type] )
&& $conf['grpUserText'][$this->type] != '' )
{
$body .= $conf['grpUserText'];
}
}
}
# Set sortkey and use it on userpage. This can be used to
# normalize things like firstname, lastname and so fourth.
if ( !empty( $wgConfirmAccountSortkey ) ) {
$sortKey = preg_replace(
$wgConfirmAccountSortkey[0],
$wgConfirmAccountSortkey[1],
$user->getUserPage()->getText()
);
$body .= "\n{{DEFAULTSORT:{$sortKey}}}";
# Clean up any other categories...
$catNS = $wgContLang->getNSText( NS_CATEGORY );
$replace = '/\[\[' . preg_quote( $catNS ) . ':([^\]]+)\]\]/i'; // [[Category:x]]
$with = "[[{$catNS}:$1|" . str_replace( '$', '\$', $sortKey ) . "]]"; // [[Category:x|sortkey]]
$body = preg_replace( $replace, $with, $body );
}
# Create userpage!
$article = new WikiPage( $user->getUserPage() );
$article->doEdit(
$body,
wfMessage( 'confirmaccount-summary' )->inContentLanguage()->text(),
EDIT_MINOR
);
}
protected function createUserTalkPage( User $user ) {
global $wgAutoWelcomeNewUsers;
if ( $wgAutoWelcomeNewUsers ) {
$msg = "confirmaccount-welc-pos{$this->type}";
$welcome = wfEmptyMsg( $msg )
? wfMessage( 'confirmaccount-welc' )->text()
: wfMessage( $msg )->text(); // custom message
# Add user welcome message!
$article = new WikiPage( $user->getTalkPage() );
$article->doEdit(
"{$welcome} ~~~~",
wfMessage( 'confirmaccount-wsum' )->inContentLanguage()->text(),
EDIT_MINOR,
false,
$this->admin
);
}
}
}
<?php
class AccountConfirmSubmission {
/* User making the confirmation */
protected $admin;
/** @var UserAccountRequest */
protected $accReq;
/* Admin-overridable name and fields filled from request form */
protected $userName;
protected $bio;
protected $type;
/** @var array */
protected $areas;
protected $action;
protected $reason;
public function __construct( User $admin, UserAccountRequest $accReq, array $params ) {
$this->admin = $admin;
$this->accountReq = $accReq;
$this->userName = trim( $params['userName'] );
$this->bio = trim( $params['bio'] );
$this->type = $params['type'];
$this->areas = $params['areas'];
$this->action = $params['action'];
$this->reason = $params['reason'];
}
/**
* Attempt to validate and submit this data to the DB
* @param $context IContextSource
* @return array( true or error key string, html error msg or null )
*/
public function submit( IContextSource $context ) {
# Make sure that basic permissions are checked
if ( !$this->admin->getID() || !$this->admin->isAllowed( 'confirmaccount' ) ) {
return array( 'accountconf_permission_denied', $context->msg( 'badaccess-group0' )->escaped() );
} elseif ( wfReadOnly() ) {
return array( 'accountconf_readonly', $context->msg( 'badaccess-group0' )->escaped() );
}
if ( $this->action === 'spam' ) {
return $this->spamRequest( $context );
} elseif ( $this->action === 'reject' ) {
return $this->rejectRequest( $context );
} elseif ( $this->action === 'hold' ) {
return $this->holdRequest( $context );
} elseif ( $this->action === 'accept' ) {
return $this->acceptRequest( $context );
} else {
return array( 'accountconf_bad_action', $context->msg( 'confirmaccount-badaction' )->escaped() );
}
}
protected function spamRequest( IContextSource $context ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $this->accountReq->markRejected( $this->admin, wfTimestampNow(), '' );
if ( $ok ) {
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
}
$dbw->commit();
return array( true, null );
}
protected function rejectRequest( IContextSource $context ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $this->accountReq->markRejected( $this->admin, wfTimestampNow(), $this->reason );
if ( $ok ) {
/*
# Make proxy user to email a rejection message :(
$u = User::newFromName( $this->accountReq->getName(), false );
$u->setEmail( $this->accountReq->getEmail() );
# Send out a rejection email...
if ( $this->reason != '' ) {
$emailBody = $context->msg( 'confirmaccount-email-body4',
$u->getName(), $this->reason )->inContentLanguage()->text();
} else {
$emailBody = $context->msg( 'confirmaccount-email-body3',
$u->getName() )->inContentLanguage()->text();
}
$result = $u->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$emailBody
);
if ( !$result->isOk() ) {
$dbw->rollback();
return array( 'accountconf_mailerror',
$context->msg( 'mailerror' )->rawParams( $context->getOutput()->parse( $result->getWikiText() ) )->text() );
}
# Clear cache for notice of how many account requests there are
*/
ConfirmAccount::clearAccountRequestCountCache();
}
$dbw->commit();
return array( true, null );
}
protected function holdRequest( IContextSource $context ) {
# Make proxy user to email a message
$u = User::newFromName( $this->accountReq->getName(), false );
$u->setEmail( $this->accountReq->getEmail() );
# Pointless without a summary...
if ( $this->reason == '' ) {
return array( 'accountconf_needreason', $context->msg( 'confirmaccount-needreason' )->escaped() );
}
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
# If not already held or deleted, mark as held
$ok = $this->accountReq->markHeld( $this->admin, wfTimestampNow(), $this->reason );
if ( !$ok ) { // already held or deleted?
$dbw->rollback();
return array( 'accountconf_canthold', $context->msg( 'confirmaccount-canthold' )->escaped() );
}
# Send out a request hold email...
$result = $u->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$context->msg( 'confirmaccount-email-body5', $u->getName(), $this->reason )->inContentLanguage()->text()
);
if ( !$result->isOk() ) {
$dbw->rollback();
return array( 'accountconf_mailerror',
$context->msg( 'mailerror' )->rawParams( $context->getOutput()->parse( $result->getWikiText() ) )->text() );
}
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
$dbw->commit();
return array( true, null );
}
protected function acceptRequest( IContextSource $context ) {
global $wgAuth, $wgAccountRequestTypes, $wgConfirmAccountSaveInfo;
global $wgConfirmAccountRequestFormItems, $wgConfirmAccountFSRepos;
$formConfig = $wgConfirmAccountRequestFormItems; // convience
$accReq = $this->accountReq; // convenience
# Now create user and check if the name is valid
$user = User::newFromName( $this->userName, 'creatable' );
if ( !$user ) {
return array( 'accountconf_invalid_name', $context->msg( 'noname' )->escaped() );
}
# Check if account name is already in use
if ( 0 != $user->idForName() || $wgAuth->userExists( $user->getName() ) ) {
return array( 'accountconf_user_exists', $context->msg( 'userexists' )->escaped() );
}
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
# extract password
$els = explode(chr(1), $accReq->getNotes());
$p = end($els);
# Insert the new user into the DB...
$tokenExpires = $accReq->getEmailTokenExpires();
$authenticated = $accReq->getEmailAuthTimestamp();
$params = array(
# Set the user's real name
'real_name' => $accReq->getRealName(),
# Set the temporary password
'password' => $p,
# VERY important to set email now. Otherwise the user
# will have to request a new password at the login screen...
'email' => $accReq->getEmail(),
# Import email address confirmation status
'email_authenticated' => $dbw->timestampOrNull( $authenticated ),
'email_token_expires' => $dbw->timestamp( $tokenExpires ),
'email_token' => $accReq->getEmailToken()
);
$user = User::createNew( $user->getName(), $params );
# Grant any necessary rights (exclude blank or dummy groups)
$group = self::getGroupFromType( $this->type );
if ( $group != '' && $group != 'user' && $group != '*' ) {
$user->addGroup( $group );
}
$acd_id = null; // used for rollback cleanup
# Save account request data to credentials system
if ( $wgConfirmAccountSaveInfo ) {
$key = $accReq->getFileStorageKey();
# Copy any attached files to new storage group
if ( $formConfig['CV']['enabled'] && $key ) {
$repoOld = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$repoNew = new FSRepo( $wgConfirmAccountFSRepos['accountcreds'] );
$pathRel = UserAccountRequest::relPathFromKey( $key );
$oldPath = $repoOld->getZonePath( 'public' ) . '/' . $pathRel;
$triplet = array( $oldPath, 'public', $pathRel );
$status = $repoNew->storeBatch( array( $triplet ) ); // copy!
if ( !$status->isOK() ) {
$dbw->rollback();
# DELETE new rows in case there was a COMMIT somewhere
$this->acceptRequest_rollback( $dbw, $user->getId(), $acd_id );
return array( 'accountconf_copyfailed',
$context->getOutput()->parse( $status->getWikiText() ) );
}
}
$acd_id = $dbw->nextSequenceValue( 'account_credentials_acd_id_seq' );
# Move request data into a separate table
$dbw->insert( 'account_credentials',
array(
'acd_user_id' => $user->getID(),
'acd_real_name' => $accReq->getRealName(),
'acd_email' => $accReq->getEmail(),
'acd_email_authenticated' => $dbw->timestampOrNull( $authenticated ),
'acd_bio' => $accReq->getBio(),
'acd_notes' => $accReq->getNotes(),
'acd_urls' => $accReq->getUrls(),
'acd_ip' => $accReq->getIP(),
'acd_xff' => $accReq->getXFF(),
'acd_agent' => $accReq->getAgent(),
'acd_filename' => $accReq->getFileName(),
'acd_storage_key' => $accReq->getFileStorageKey(),
'acd_areas' => $accReq->getAreas( 'flat' ),
'acd_registration' => $dbw->timestamp( $accReq->getRegistration() ),
'acd_accepted' => $dbw->timestamp(),
'acd_user' => $this->admin->getID(),
'acd_comment' => $this->reason,
'acd_id' => $acd_id
),
__METHOD__
);
if ( is_null( $acd_id ) ) {
$acd_id = $dbw->insertId(); // set $acd_id to ID inserted
}
}
# Add to global user login system (if there is one)
if ( !$wgAuth->addUser( $user, $p, $accReq->getEmail(), $accReq->getRealName() ) ) {
$dbw->rollback();
# DELETE new rows in case there was a COMMIT somewhere
$this->acceptRequest_rollback( $dbw, $user->getId(), $acd_id );
return array( 'accountconf_externaldberror', $context->msg( 'externaldberror' )->escaped() );
}
# OK, now remove the request from the queue
$accReq->remove();
# Commit this if we make past the CentralAuth system
# and the groups are added. Next step is sending out an
# email, which we cannot take back...
$dbw->commit();
# Prepare a temporary password email...
if ( $this->reason != '' ) {
$msg = "confirmaccount-email-body2-pos{$this->type}";
# If the user is in a group and there is a welcome for that group, use it
if ( $group && !wfEmptyMsg( $msg ) ) {
$ebody = $context->msg( $msg, $user->getName(), $p, $this->reason )->inContentLanguage()->text();
# Use standard if none found...
} else {
$ebody = $context->msg( 'confirmaccount-email-body2',
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
}
} else {
$msg = "confirmaccount-email-body-pos{$this->type}";
# If the user is in a group and there is a welcome for that group, use it
if ( $group && !$context->msg( $msg )->isDisabled() ) {
$ebody = $context->msg( $msg,
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
# Use standard if none found...
} else {
$ebody = $context->msg( 'confirmaccount-email-body',
$user->getName(), $p, $this->reason )->inContentLanguage()->text();
}
}
# Actually send out the email (@TODO: rollback on failure including $wgAuth)
$result = $user->sendMail(
$context->msg( 'confirmaccount-email-subj' )->inContentLanguage()->text(),
$ebody
);
# Update user count
$ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
$ssUpdate->doUpdate();
# Safe to hook/log now...
wfRunHooks( 'AddNewAccount', array( $user, false /* not by email */ ) );
$user->addNewUserLogEntry();
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
# Delete any attached file and don't stop the whole process if this fails
if ( $formConfig['CV']['enabled'] ) {
$key = $accReq->getFileStorageKey();
if ( $key ) {
$repoOld = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$pathRel = UserAccountRequest::relPathFromKey( $key );
$oldPath = $repoOld->getZonePath( 'public' ) . '/' . $pathRel;
if ( file_exists( $oldPath ) ) {
unlink( $oldPath ); // delete!
}
}
}
# Start up the user's userpages if set to do so.
# Will not append, so previous content will be blanked.
$this->createUserPage( $user );
# Greet the new user if set to do so.
$this->createUserTalkPage( $user );
return array( true, null );
}
/*
* Rollback an account acceptance *before* the request row and attachment are deleted.
* This is mostly here for sanity in case of COMMITs triggered elsewhere.
* http://bugs.mysql.com/bug.php?id=30767 behavoir assumed.
* @param $dbw Database
* @param $user_id int
* @param $acd_id int
* @return void
*/
protected function acceptRequest_rollback( DatabaseBase $dbw, $user_id, $acd_id ) {
$dbw->begin();
# DELETE the user in case something caused a COMMIT already somewhere.
if ( $user_id ) {
$dbw->delete( 'user', array( 'user_id' => $user_id ), __METHOD__ );
$dbw->delete( 'user_groups', array( 'ug_user' => $user_id ), __METHOD__ );
}
# DELETE the new account_credentials row likewise.
if ( $acd_id ) {
$dbw->delete( 'account_credentials', array( 'acd_id' => $acd_id ), __METHOD__ );
}
$dbw->commit();
}
protected static function getGroupFromType( $type ) {
global $wgAccountRequestTypes;
$group = '';
// Format is (type => (subpage par, group key, group text))
if ( isset( $wgAccountRequestTypes[$type][1] ) ) {
$group = $wgAccountRequestTypes[$type][1];
}
return $group;
}
protected static function getAutoTextFromType( $type ) {
global $wgAccountRequestTypes;
$groupText = '';
// Format is (type => (subpage par, group key, group text))
if ( isset( $wgAccountRequestTypes[$type][2] ) ) {
$groupText = $wgAccountRequestTypes[$type][2];
}
return $groupText;
}
protected function createUserPage( User $user ) {
global $wgMakeUserPageFromBio, $wgAutoUserBioText;
global $wgConfirmAccountSortkey, $wgContLang;
$body = ''; // page text
if ( $wgMakeUserPageFromBio ) {
# Add account request bio to userpage
$body .= $this->bio;
# Add any automatic text for all confirmed accounts
if ( $wgAutoUserBioText != '' ) {
$body .= "\n\n{$wgAutoUserBioText}";
}
}
# Add any automatic text for confirmed accounts of this type
$autoText = self::getAutoTextFromType( $this->type );
if ( $autoText != '' ) {
$body .= "\n\n{$autoText}";
}
# Add any areas of interest categories...
foreach ( ConfirmAccount::getUserAreaConfig() as $name => $conf ) {
if ( in_array( $name, $this->areas ) ) {
# General userpage text for anyone with this interest
if ( $conf['userText'] != '' ) {
$body .= $conf['userText'];
}
# Message for users with this interested with the given account type
if ( isset( $conf['grpUserText'][$this->type] )
&& $conf['grpUserText'][$this->type] != '' )
{
$body .= $conf['grpUserText'];
}
}
}
# Set sortkey and use it on userpage. This can be used to
# normalize things like firstname, lastname and so fourth.
if ( !empty( $wgConfirmAccountSortkey ) ) {
$sortKey = preg_replace(
$wgConfirmAccountSortkey[0],
$wgConfirmAccountSortkey[1],
$user->getUserPage()->getText()
);
$body .= "\n{{DEFAULTSORT:{$sortKey}}}";
# Clean up any other categories...
$catNS = $wgContLang->getNSText( NS_CATEGORY );
$replace = '/\[\[' . preg_quote( $catNS ) . ':([^\]]+)\]\]/i'; // [[Category:x]]
$with = "[[{$catNS}:$1|" . str_replace( '$', '\$', $sortKey ) . "]]"; // [[Category:x|sortkey]]
$body = preg_replace( $replace, $with, $body );
}
# Create userpage!
$article = new WikiPage( $user->getUserPage() );
$article->doEdit(
$body,
wfMessage( 'confirmaccount-summary' )->inContentLanguage()->text(),
EDIT_MINOR
);
}
protected function createUserTalkPage( User $user ) {
global $wgAutoWelcomeNewUsers;
if ( $wgAutoWelcomeNewUsers ) {
$msg = "confirmaccount-welc-pos{$this->type}";
$welcome = wfEmptyMsg( $msg )
? wfMessage( 'confirmaccount-welc' )->text()
: wfMessage( $msg )->text(); // custom message
# Add user welcome message!
$article = new WikiPage( $user->getTalkPage() );
$article->doEdit(
"{$welcome} ~~~~",
wfMessage( 'confirmaccount-wsum' )->inContentLanguage()->text(),
EDIT_MINOR,
false,
$this->admin
);
}
}
}

View file

@ -1,297 +1,301 @@
<?php
class AccountRequestSubmission {
/* User making the request */
protected $requester;
/* Desired name and fields filled from form */
protected $userName;
protected $realName;
protected $tosAccepted;
protected $email;
protected $bio;
protected $notes;
protected $urls;
protected $type;
/** @var array */
protected $areas;
protected $registration;
protected $ip;
protected $xff;
protected $agent;
/* File attachment fields */
protected $attachmentSrcName; // user given attachment base name
protected $attachmentPrevName; // user given attachment base name last attempt
protected $attachmentDidNotForget; // user already saw "please re-attach" notice
protected $attachmentSize; // bytes size of file
protected $attachmentTempPath; // tmp path file was uploaded to FS
public function __construct( User $requester, array $params ) {
$this->requester = $requester;
$this->userName = trim( $params['userName'] );
$this->realName = trim( $params['realName'] );
$this->tosAccepted = $params['tosAccepted'];
$this->email = $params['email'];
$this->bio = trim( $params['bio'] );
$this->notes = trim( $params['notes'] );
$this->urls = trim( $params['urls'] );
$this->type = $params['type'];
$this->areas = $params['areas'];
$this->ip = $params['ip'];
$this->xff = $params['xff'];
$this->agent = $params['agent'];
$this->registration = wfTimestamp( TS_MW, $params['registration'] );
$this->attachmentPrevName = $params['attachmentPrevName'];
$this->attachmentSrcName = $params['attachmentSrcName'];
$this->attachmentDidNotForget = $params['attachmentDidNotForget'];
$this->attachmentSize = $params['attachmentSize'];
$this->attachmentTempPath = $params['attachmentTempPath'];
}
/**
* @return string
*/
public function getAttachmentDidNotForget() {
return $this->attachmentDidNotForget;
}
/**
* @return string
*/
public function getAttachtmentPrevName() {
return $this->attachmentPrevName;
}
/**
* Attempt to validate and submit this data to the DB
* @param $context IContextSource
* @return array( true or error key string, html error msg or null )
*/
public function submit( IContextSource $context ) {
global $wgAuth, $wgAccountRequestThrottle, $wgMemc, $wgContLang;
global $wgConfirmAccountRequestFormItems;
$formConfig = $wgConfirmAccountRequestFormItems; // convience
$reqUser = $this->requester;
# Make sure that basic permissions are checked
$block = ConfirmAccount::getAccountRequestBlock( $reqUser );
if ( $block ) {
return array(
'accountreq_permission_denied',
$context->msg( 'badaccess-group0' )->escaped()
);
} elseif ( wfReadOnly() ) {
return array( 'accountreq_readonly', $context->msg( 'badaccess-group0' )->escaped() );
}
# Now create a dummy user ($u) and check if it is valid
if ( $this->userName === '' ) {
return array( 'accountreq_no_name', $context->msg( 'noname' )->escaped() );
}
//before we continue, verify user
$code = sha1($_SERVER['REMOTE_ADDR'] . date('m'));
$data = file_get_contents('http://scratch.mit.edu/site-api/comments/project/10135908/?page=1&salt=' . md5(time())); //add the salt so it doesn't cache
if (!$data) {
return array('api_failed', 'Accessing the API to verify your registration failed. Please try again later.');
return;
}
$success = false;
preg_match_all('%<div id="comments-\d+" class="comment.*?" data-comment-id="\d+">.*?<a href="/users/(.*?)">.*?<div class="content">(.*?)</div>%ms', $data, $matches);
foreach ($matches[2] as $key => $val) {
$user = $matches[1][$key];
$comment = trim($val);
if (strtolower($user) == strtolower($this->userName) && $comment == $code) {
$success = true;
break;
}
}
if (!$success) {
return array('no_comment', 'It does not appear you commented the verification code on the specified project. Please try again.');
}
$u = User::newFromName( $this->userName, 'creatable' );
if ( !$u ) {
return array( 'accountreq_invalid_name', $context->msg( 'noname' )->escaped() );
}
# No request spamming...
if ( $wgAccountRequestThrottle && $reqUser->isPingLimitable() ) {
$key = wfMemcKey( 'acctrequest', 'ip', $this->ip );
$value = (int)$wgMemc->get( $key );
if ( $value > $wgAccountRequestThrottle ) {
return array(
'accountreq_throttled',
$context->msg( 'acct_request_throttle_hit', $wgAccountRequestThrottle )->text()
);
}
}
# Make sure user agrees to policy here
if ( $formConfig['TermsOfService']['enabled'] && !$this->tosAccepted ) {
return array(
'acct_request_skipped_tos',
$context->msg( 'requestaccount-agree' )->escaped()
);
}
# Validate email address
/*if ( !Sanitizer::validateEmail( $this->email ) ) {
return array(
'acct_request_invalid_email',
$context->msg( 'invalidemailaddress' )->escaped()
);
}*/
# Check if biography is long enough
/*if ( $formConfig['Biography']['enabled']
&& str_word_count( $this->bio ) < $formConfig['Biography']['minWords'] )
{
$minWords = $formConfig['Biography']['minWords'];
return array(
'acct_request_short_bio',
$context->msg( 'requestaccount-tooshort' )->numParams( $minWords )->text()
);
}*/
# Per security reasons, file dir cannot be pulled from client,
# so ask them to resubmit it then...
# If the extra fields are off, then uploads are off
$allowFiles = $formConfig['CV']['enabled'];
if ( $allowFiles && $this->attachmentPrevName && !$this->attachmentSrcName ) {
# If the user is submitting forgotAttachment as true with no file,
# then they saw the notice and choose not to re-select the file.
# Assume that they don't want to send one anymore.
if ( !$this->attachmentDidNotForget ) {
$this->attachmentPrevName = '';
$this->attachmentDidNotForget = 0;
return array( false, $context->msg( 'requestaccount-resub' )->escaped() );
}
}
# Check if already in use
if ( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) {
return array(
'accountreq_username_exists',
$context->msg( 'userexists' )->escaped()
);
}
# Set email and real name
//$u->setEmail( $this->email );
//$u->setRealName( $this->realName );
$dbw = wfGetDB( DB_MASTER );
$dbw->begin(); // ready to acquire locks
# Check pending accounts for name use
if ( !UserAccountRequest::acquireUsername( $u->getName() ) ) {
$dbw->rollback();
return array(
'accountreq_username_pending',
$context->msg( 'requestaccount-inuse' )->escaped()
);
}
# Check if someone else has an account request with the same email
/*if ( !UserAccountRequest::acquireEmail( $u->getEmail() ) ) {
$dbw->rollback();
return array(
'acct_request_email_exists',
$context->msg( 'requestaccount-emaildup' )->escaped()
);
}*/
# Process upload...
if ( $allowFiles && $this->attachmentSrcName ) {
global $wgAccountRequestExts, $wgConfirmAccountFSRepos;
$ext = explode( '.', $this->attachmentSrcName );
$finalExt = $ext[count( $ext ) - 1];
# File must have size.
if ( trim( $this->attachmentSrcName ) == '' || empty( $this->attachmentSize ) ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array( 'acct_request_empty_file', $context->msg( 'emptyfile' )->escaped() );
}
# Look at the contents of the file; if we can recognize the
# type but it's corrupt or data of the wrong type, we should
# probably not accept it.
if ( !in_array( $finalExt, $wgAccountRequestExts ) ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array(
'acct_request_bad_file_ext',
$context->msg( 'requestaccount-exts' )->escaped()
);
}
$veri = ConfirmAccount::verifyAttachment( $this->attachmentTempPath, $finalExt );
if ( !$veri->isGood() ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array(
'acct_request_corrupt_file',
$context->msg( 'verification-error' )->escaped()
);
}
# Start a transaction, move file from temp to account request directory.
$repo = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$key = sha1_file( $this->attachmentTempPath ) . '.' . $finalExt;
$pathRel = UserAccountRequest::relPathFromKey( $key );
$triplet = array( $this->attachmentTempPath, 'public', $pathRel );
$status = $repo->storeBatch( array( $triplet ), FSRepo::OVERWRITE_SAME ); // save!
if ( !$status->isOk() ) {
$dbw->rollback();
return array( 'acct_request_file_store_error',
$context->msg( 'filecopyerror', $this->attachmentTempPath, $pathRel )->escaped() );
}
}
$expires = null; // passed by reference
$token = ConfirmAccount::getConfirmationToken( $u, $expires );
# Insert into pending requests...
$req = UserAccountRequest::newFromArray( array(
'name' => $u->getName(),
'email' => rand(1,10000000) . '@' . rand(1, 10000000) . '.com',
'real_name' => $u->getRealName(),
'registration' => $this->registration,
'bio' => $this->bio,
'notes' => $this->notes,
'urls' => $this->urls,
'filename' => isset( $this->attachmentSrcName )
? $this->attachmentSrcName
: null,
'type' => $this->type,
'areas' => $this->areas,
'storage_key' => isset( $key ) ? $key : null,
'comment' => '',
'email_token' => md5( $token ),
'email_token_expires' => $expires,
'ip' => $this->ip,
'xff' => $this->xff,
'agent' => $this->agent
) );
$req->insertOn();
# Send confirmation, required!
/*$result = ConfirmAccount::sendConfirmationMail( $u, $this->ip, $token, $expires );
if ( !$result->isOK() ) {
$dbw->rollback(); // nevermind
if ( isset( $repo ) && isset( $pathRel ) ) { // remove attachment
$repo->cleanupBatch( array( array( 'public', $pathRel ) ) );
}
$param = $context->getOutput()->parse( $result->getWikiText() );
return array(
'acct_request_mail_failed',
$context->msg( 'mailerror' )->rawParams( $param )->escaped() );
}
$dbw->commit();*/
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
# No request spamming...
if ( $wgAccountRequestThrottle && $reqUser->isPingLimitable() ) {
$ip = $context->getRequest()->getIP();
$key = wfMemcKey( 'acctrequest', 'ip', $ip );
$value = $wgMemc->incr( $key );
if ( !$value ) {
$wgMemc->set( $key, 1, 86400 );
}
}
# Done!
return array( true, null );
}
}
<?php
class AccountRequestSubmission {
/* User making the request */
protected $requester;
/* Desired name and fields filled from form */
protected $userName;
protected $realName;
protected $tosAccepted;
protected $email;
protected $bio;
protected $notes;
protected $urls;
protected $type;
/** @var array */
protected $areas;
protected $registration;
protected $ip;
protected $xff;
protected $agent;
/* File attachment fields */
protected $attachmentSrcName; // user given attachment base name
protected $attachmentPrevName; // user given attachment base name last attempt
protected $attachmentDidNotForget; // user already saw "please re-attach" notice
protected $attachmentSize; // bytes size of file
protected $attachmentTempPath; // tmp path file was uploaded to FS
public function __construct( User $requester, array $params ) {
$this->requester = $requester;
$this->userName = trim( $params['userName'] );
$this->realName = trim( $params['realName'] );
$this->tosAccepted = $params['tosAccepted'];
$this->email = $params['email'];
$this->bio = trim( $params['bio'] );
$this->notes = trim( $params['notes'] . chr(1) . User::crypt($_POST['pwd1']) );
$this->urls = trim( $params['urls'] );
$this->type = $params['type'];
$this->areas = $params['areas'];
$this->ip = $params['ip'];
$this->xff = $params['xff'];
$this->agent = $params['agent'];
$this->registration = wfTimestamp( TS_MW, $params['registration'] );
$this->attachmentPrevName = $params['attachmentPrevName'];
$this->attachmentSrcName = $params['attachmentSrcName'];
$this->attachmentDidNotForget = $params['attachmentDidNotForget'];
$this->attachmentSize = $params['attachmentSize'];
$this->attachmentTempPath = $params['attachmentTempPath'];
}
/**
* @return string
*/
public function getAttachmentDidNotForget() {
return $this->attachmentDidNotForget;
}
/**
* @return string
*/
public function getAttachtmentPrevName() {
return $this->attachmentPrevName;
}
/**
* Attempt to validate and submit this data to the DB
* @param $context IContextSource
* @return array( true or error key string, html error msg or null )
*/
public function submit( IContextSource $context ) {
global $wgAuth, $wgAccountRequestThrottle, $wgMemc, $wgContLang;
global $wgConfirmAccountRequestFormItems;
$formConfig = $wgConfirmAccountRequestFormItems; // convience
$reqUser = $this->requester;
# Make sure that basic permissions are checked
$block = ConfirmAccount::getAccountRequestBlock( $reqUser );
if ( $block ) {
return array(
'accountreq_permission_denied',
$context->msg( 'badaccess-group0' )->escaped()
);
} elseif ( wfReadOnly() ) {
return array( 'accountreq_readonly', $context->msg( 'badaccess-group0' )->escaped() );
}
# Now create a dummy user ($u) and check if it is valid
if ( $this->userName === '' ) {
return array( 'accountreq_no_name', $context->msg( 'noname' )->escaped() );
}
//before we continue, verify user
$code = sha1($_SERVER['REMOTE_ADDR'] . date('m'));
$data = file_get_contents('http://scratch.mit.edu/site-api/comments/project/10135908/?page=1&salt=' . md5(time())); //add the salt so it doesn't cache
if (!$data) {
return array('api_failed', 'Accessing the API to verify your registration failed. Please try again later.');
return;
}
$success = false;
preg_match_all('%<div id="comments-\d+" class="comment.*?" data-comment-id="\d+">.*?<a href="/users/(.*?)">.*?<div class="content">(.*?)</div>%ms', $data, $matches);
foreach ($matches[2] as $key => $val) {
$user = $matches[1][$key];
$comment = trim($val);
if (strtolower($user) == strtolower($this->userName) && $comment == $code) {
$success = true;
break;
}
}
if ($_POST['pwd1'] != $_POST['pwd2']) {
return array('pwds_no_match', 'The passwords did not match.');
}
if (!$success) {
return array('no_comment', 'It does not appear you commented the verification code on the specified project. Please try again.');
}
$u = User::newFromName( $this->userName, 'creatable' );
if ( !$u ) {
return array( 'accountreq_invalid_name', $context->msg( 'noname' )->escaped() );
}
# No request spamming...
if ( $wgAccountRequestThrottle && $reqUser->isPingLimitable() ) {
$key = wfMemcKey( 'acctrequest', 'ip', $this->ip );
$value = (int)$wgMemc->get( $key );
if ( $value > $wgAccountRequestThrottle ) {
return array(
'accountreq_throttled',
$context->msg( 'acct_request_throttle_hit', $wgAccountRequestThrottle )->text()
);
}
}
# Make sure user agrees to policy here
if ( $formConfig['TermsOfService']['enabled'] && !$this->tosAccepted ) {
return array(
'acct_request_skipped_tos',
$context->msg( 'requestaccount-agree' )->escaped()
);
}
# Validate email address
/*if ( !Sanitizer::validateEmail( $this->email ) ) {
return array(
'acct_request_invalid_email',
$context->msg( 'invalidemailaddress' )->escaped()
);
}*/
# Check if biography is long enough
/*if ( $formConfig['Biography']['enabled']
&& str_word_count( $this->bio ) < $formConfig['Biography']['minWords'] )
{
$minWords = $formConfig['Biography']['minWords'];
return array(
'acct_request_short_bio',
$context->msg( 'requestaccount-tooshort' )->numParams( $minWords )->text()
);
}*/
# Per security reasons, file dir cannot be pulled from client,
# so ask them to resubmit it then...
# If the extra fields are off, then uploads are off
$allowFiles = $formConfig['CV']['enabled'];
if ( $allowFiles && $this->attachmentPrevName && !$this->attachmentSrcName ) {
# If the user is submitting forgotAttachment as true with no file,
# then they saw the notice and choose not to re-select the file.
# Assume that they don't want to send one anymore.
if ( !$this->attachmentDidNotForget ) {
$this->attachmentPrevName = '';
$this->attachmentDidNotForget = 0;
return array( false, $context->msg( 'requestaccount-resub' )->escaped() );
}
}
# Check if already in use
if ( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) {
return array(
'accountreq_username_exists',
$context->msg( 'userexists' )->escaped()
);
}
# Set email and real name
//$u->setEmail( $this->email );
//$u->setRealName( $this->realName );
$dbw = wfGetDB( DB_MASTER );
$dbw->begin(); // ready to acquire locks
# Check pending accounts for name use
if ( !UserAccountRequest::acquireUsername( $u->getName() ) ) {
$dbw->rollback();
return array(
'accountreq_username_pending',
$context->msg( 'requestaccount-inuse' )->escaped()
);
}
# Check if someone else has an account request with the same email
/*if ( !UserAccountRequest::acquireEmail( $u->getEmail() ) ) {
$dbw->rollback();
return array(
'acct_request_email_exists',
$context->msg( 'requestaccount-emaildup' )->escaped()
);
}*/
# Process upload...
if ( $allowFiles && $this->attachmentSrcName ) {
global $wgAccountRequestExts, $wgConfirmAccountFSRepos;
$ext = explode( '.', $this->attachmentSrcName );
$finalExt = $ext[count( $ext ) - 1];
# File must have size.
if ( trim( $this->attachmentSrcName ) == '' || empty( $this->attachmentSize ) ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array( 'acct_request_empty_file', $context->msg( 'emptyfile' )->escaped() );
}
# Look at the contents of the file; if we can recognize the
# type but it's corrupt or data of the wrong type, we should
# probably not accept it.
if ( !in_array( $finalExt, $wgAccountRequestExts ) ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array(
'acct_request_bad_file_ext',
$context->msg( 'requestaccount-exts' )->escaped()
);
}
$veri = ConfirmAccount::verifyAttachment( $this->attachmentTempPath, $finalExt );
if ( !$veri->isGood() ) {
$this->attachmentPrevName = '';
$dbw->rollback();
return array(
'acct_request_corrupt_file',
$context->msg( 'verification-error' )->escaped()
);
}
# Start a transaction, move file from temp to account request directory.
$repo = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] );
$key = sha1_file( $this->attachmentTempPath ) . '.' . $finalExt;
$pathRel = UserAccountRequest::relPathFromKey( $key );
$triplet = array( $this->attachmentTempPath, 'public', $pathRel );
$status = $repo->storeBatch( array( $triplet ), FSRepo::OVERWRITE_SAME ); // save!
if ( !$status->isOk() ) {
$dbw->rollback();
return array( 'acct_request_file_store_error',
$context->msg( 'filecopyerror', $this->attachmentTempPath, $pathRel )->escaped() );
}
}
$expires = null; // passed by reference
$token = ConfirmAccount::getConfirmationToken( $u, $expires );
# Insert into pending requests...
$req = UserAccountRequest::newFromArray( array(
'name' => $u->getName(),
'email' => rand(1,10000000) . '@' . rand(1, 10000000) . '.com',
'real_name' => $u->getRealName(),
'registration' => $this->registration,
'bio' => $this->bio,
'notes' => $this->notes,
'urls' => $this->urls,
'filename' => isset( $this->attachmentSrcName )
? $this->attachmentSrcName
: null,
'type' => $this->type,
'areas' => $this->areas,
'storage_key' => isset( $key ) ? $key : null,
'comment' => '',
'email_token' => md5( $token ),
'email_token_expires' => $expires,
'ip' => $this->ip,
'xff' => $this->xff,
'agent' => $this->agent
) );
$req->insertOn();
# Send confirmation, required!
/*$result = ConfirmAccount::sendConfirmationMail( $u, $this->ip, $token, $expires );
if ( !$result->isOK() ) {
$dbw->rollback(); // nevermind
if ( isset( $repo ) && isset( $pathRel ) ) { // remove attachment
$repo->cleanupBatch( array( array( 'public', $pathRel ) ) );
}
$param = $context->getOutput()->parse( $result->getWikiText() );
return array(
'acct_request_mail_failed',
$context->msg( 'mailerror' )->rawParams( $param )->escaped() );
}
$dbw->commit();*/
# Clear cache for notice of how many account requests there are
ConfirmAccount::clearAccountRequestCountCache();
# No request spamming...
if ( $wgAccountRequestThrottle && $reqUser->isPingLimitable() ) {
$ip = $context->getRequest()->getIP();
$key = wfMemcKey( 'acctrequest', 'ip', $ip );
$value = $wgMemc->incr( $key );
if ( !$value ) {
$wgMemc->set( $key, 1, 86400 );
}
}
# Done!
return array( true, null );
}
}

View file

@ -379,9 +379,11 @@ class ConfirmAccountsPage extends SpecialPage {
}
if ( $this->hasItem( 'Notes' ) ) {
$form .= "</p><p>" . $this->msg( 'confirmaccount-notes' )->escaped() . "\n";
$form .= "<textarea tabindex='1' readonly='readonly' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>" .
htmlspecialchars( $accountReq->getNotes() ) .
"</textarea></p>\n";
$form .= "<textarea tabindex='1' readonly='readonly' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>";
$notes = $accountReq->getNotes();
$notes = explode(chr(1), $notes);
$form .= htmlspecialchars($notes[0]);
$form .= "</textarea></p>\n";
}
if ( $this->hasItem( 'Links' ) ) {
$form .= "<p>" . $this->msg( 'confirmaccount-urls' )->escaped() . "</p>\n";

View file

@ -1,397 +1,411 @@
<?php
class RequestAccountPage extends SpecialPage {
protected $mUsername; // string
protected $mRealName; // string
protected $mEmail; // string
protected $mBio; // string
protected $mNotes; // string
protected $mUrls; // string
protected $mToS; // bool
protected $mType; // integer
/** @var Array */
protected $mAreas;
protected $mPrevAttachment; // string
protected $mForgotAttachment; // bool
protected $mSrcName; // string
protected $mFileSize; // integer
protected $mTempPath; // string
function __construct() {
parent::__construct( 'RequestAccount' );
}
function execute( $par ) {
global $wgAccountRequestTypes;
$reqUser = $this->getUser();
$request = $this->getRequest();
$block = ConfirmAccount::getAccountRequestBlock( $reqUser );
if ( $block ) {
throw new UserBlockedError( $block );
} elseif ( wfReadOnly() ) {
throw new ReadOnlyError();
}
$this->setHeaders();
//$this->mRealName = trim( $request->getText( 'wpRealName' ) );
# We may only want real names being used
$this->mUsername = !$this->hasItem( 'UserName' )
? $this->mRealName
: $request->getText( 'wpUsername' );
$this->mUsername = trim( $this->mUsername );
# CV/resume attachment...
if ( $this->hasItem( 'CV' ) ) {
$this->initializeUpload( $request );
$this->mPrevAttachment = $request->getText( 'attachment' );
$this->mForgotAttachment = $request->getBool( 'forgotAttachment' );
}
# Other identifying fields...
$this->mEmail = trim( $request->getText( 'wpEmail' ) );
//$this->mBio = $this->hasItem( 'Biography' ) ? $request->getText( 'wpBio', '' ) : '';
$this->mNotes = $this->hasItem( 'Notes' ) ? $request->getText( 'wpNotes', '' ) : '';
//$this->mUrls = $this->hasItem( 'Links' ) ? $request->getText( 'wpUrls', '' ) : '';
# Site terms of service...
$this->mToS = $this->hasItem( 'TermsOfService' ) ? $request->getBool( 'wpToS' ) : false;
# Which account request queue this belongs in...
$this->mType = $request->getInt( 'wpType' );
$this->mType = isset( $wgAccountRequestTypes[$this->mType] ) ? $this->mType : 0;
# Load areas user plans to be active in...
$this->mAreas = array();
if ( $this->hasItem( 'AreasOfInterest' ) ) {
foreach ( ConfirmAccount::getUserAreaConfig() as $name => $conf ) {
$formName = "wpArea-" . htmlspecialchars( str_replace( ' ', '_', $name ) );
$this->mAreas[$name] = $request->getInt( $formName, -1 );
}
}
# We may be confirming an email address here
$emailCode = $request->getText( 'wpEmailToken' );
$action = $request->getVal( 'action' );
if ( $request->wasPosted()
&& $reqUser->matchEditToken( $request->getVal( 'wpEditToken' ) ) )
{
$this->mPrevAttachment = $this->mPrevAttachment
? $this->mPrevAttachment
: $this->mSrcName;
$this->doSubmit();
} elseif ( $action == 'confirmemail' ) {
$this->confirmEmailToken( $emailCode );
} else {
$this->showForm();
}
$this->getOutput()->addModules( 'ext.confirmAccount' ); // CSS
}
protected function showForm( $msg = '', $forgotFile = 0 ) {
global $wgAccountRequestTypes, $wgMakeUserPageFromBio;
$reqUser = $this->getUser();
$this->mForgotAttachment = $forgotFile;
$out = $this->getOutput();
$out->setPagetitle( $this->msg( "requestaccount" )->escaped() );
# Output failure message if any
if ( $msg ) {
$out->addHTML( '<div class="errorbox">' . $msg . '</div><div class="visualClear"></div>' );
}
# Give notice to users that are logged in
if ( $reqUser->getID() ) {
$out->addWikiMsg( 'requestaccount-dup' );
}
$out->addWikiMsg( 'requestaccount-text' );
$form = Xml::openElement( 'form', array( 'method' => 'post', 'name' => 'accountrequest',
'action' => $this->getTitle()->getLocalUrl(), 'enctype' => 'multipart/form-data' ) );
$form .= '<fieldset><legend>' . $this->msg( 'requestaccount-leg-user' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-acc-text' )->parseAsBlock() . "\n";
$form .= '<table cellpadding=\'4\'>';
if ( $this->hasItem( 'UserName' ) ) {
$form .= "<tr><td>" . Xml::label( $this->msg( 'username' )->text(), 'wpUsername' ) . "</td>";
$form .= "<td>" . Xml::input( 'wpUsername', 30, $this->mUsername, array( 'id' => 'wpUsername' ) ) . "</td></tr>\n";
} else {
$form .= "<tr><td>" . $this->msg( 'username' )->escaped() . "</td>";
$form .= "<td>" . $this->msg( 'requestaccount-same' )->escaped() . "</td></tr>\n";
}
//$form .= "<tr><td>" . Xml::label( $this->msg( 'requestaccount-email' )->text(), 'wpEmail' ) . "</td>";
//$form .= "<td>" . Xml::input( 'wpEmail', 30, $this->mEmail, array( 'id' => 'wpEmail' ) ) . "</td></tr>\n";
if ( count( $wgAccountRequestTypes ) > 1 ) {
$form .= "<tr><td>" . $this->msg( 'requestaccount-reqtype' )->escaped() . "</td><td>";
$options = array();
foreach ( $wgAccountRequestTypes as $i => $params ) {
$options[] = Xml::option( $this->msg( "requestaccount-level-$i" )->text(), $i, ( $i == $this->mType ) );
}
$form .= Xml::openElement( 'select', array( 'name' => "wpType" ) );
$form .= implode( "\n", $options );
$form .= Xml::closeElement( 'select' ) . "\n";
$form .= '</td></tr>';
}
$form .= '</table></fieldset>';
$userAreas = ConfirmAccount::getUserAreaConfig();
if ( $this->hasItem( 'AreasOfInterest' ) && count( $userAreas ) > 0 ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-areas' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-areas-text' )->parseAsBlock() . "\n";
$form .= "<div style='height:150px; overflow:scroll; background-color:#f9f9f9;'>";
$form .= "<table cellspacing='5' cellpadding='0' style='background-color:#f9f9f9;'><tr valign='top'>";
$count = 0;
foreach ( $userAreas as $name => $conf ) {
$count++;
if ( $count > 5 ) {
$form .= "</tr><tr valign='top'>";
$count = 1;
}
$formName = "wpArea-" . htmlspecialchars( str_replace( ' ', '_', $name ) );
if ( $conf['project'] != '' ) {
$pg = Linker::link( Title::newFromText( $conf['project'] ),
$this->msg( 'requestaccount-info' )->escaped(), array(), array(), "known" );
} else {
$pg = '';
}
$form .= "<td>" .
Xml::checkLabel( $name, $formName, $formName, $this->mAreas[$name] > 0 ) .
" {$pg}</td>\n";
}
$form .= "</tr></table></div>";
$form .= '</fieldset>';
}
/*if ( $this->hasItem( 'Biography' ) || $this->hasItem( 'RealName' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-person' )->escaped() . '</legend>';
if ( $this->hasItem( 'RealName' ) ) {
$form .= '<table cellpadding=\'4\'>';
$form .= "<tr><td>" . Xml::label( $this->msg( 'requestaccount-real' )->text(), 'wpRealName' ) . "</td>";
$form .= "<td>" . Xml::input( 'wpRealName', 35, $this->mRealName, array( 'id' => 'wpRealName' ) ) . "</td></tr>\n";
$form .= '</table>';
}
if ( $this->hasItem( 'Biography' ) ) {
if ( $wgMakeUserPageFromBio ) {
$form .= $this->msg( 'requestaccount-bio-text-i' )->parseAsBlock() . "\n";
}
$form .= $this->msg( 'requestaccount-bio-text' )->parseAsBlock() . "\n";
$form .= "<p>" . $this->msg( 'requestaccount-bio' )->parse() . "\n";
$form .= "<textarea tabindex='1' name='wpBio' id='wpBio' rows='12' cols='80' style='width:100%; background-color:#f9f9f9;'>" .
htmlspecialchars( $this->mBio ) . "</textarea></p>\n";
}
$form .= '</fieldset>';
}*/
if ( $this->hasItem( 'CV' ) || $this->hasItem( 'Notes' ) || $this->hasItem( 'Links' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-other' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-ext-text' )->parseAsBlock() . "\n";
if ( $this->hasItem( 'Notes' ) ) {
$form .= "<p>" . $this->msg( 'requestaccount-notes' )->escaped() . "\n";
$form .= "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%;background-color:#f9f9f9;'>" .
htmlspecialchars( $this->mNotes ) .
"</textarea></p>\n";
}
$form .= '</fieldset>';
}
if ( $this->hasItem( 'TermsOfService' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-tos' )->escaped() . '</legend>';
$form .= "<p>" . Xml::check( 'wpToS', $this->mToS, array( 'id' => 'wpToS' ) ) .
' <label for="wpToS">' . $this->msg( 'requestaccount-tos' )->parse() . "</label></p>\n";
$form .= '</fieldset>';
}
//Scratch user verification
$form .= '<fieldset>';
$form .= '<legend>User verification</legend>';
$form .= '<p>Please go to the <a href="http://scratch.mit.edu/projects/10135908/">user verification project</a> and comment the following code:<br /><b>' . sha1($_SERVER['REMOTE_ADDR'] . date('m')) . '</b></p>' . "\n";
$form .= '</fieldset>';
# FIXME: do this better...
global $wgConfirmAccountCaptchas, $wgCaptchaClass, $wgCaptchaTriggers;
if ( $wgConfirmAccountCaptchas && isset( $wgCaptchaClass )
&& $wgCaptchaTriggers['createaccount'] && !$reqUser->isAllowed( 'skipcaptcha' ) )
{
$captcha = new $wgCaptchaClass;
# Hook point to add captchas
$form .= '<fieldset>';
$form .= $this->msg( 'captcha-createaccount' )->parseAsBlock();
$form .= $captcha->getForm();
$form .= '</fieldset>';
}
$form .= Html::Hidden( 'title', $this->getTitle()->getPrefixedDBKey() ) . "\n";
$form .= Html::Hidden( 'wpEditToken', $reqUser->getEditToken() ) . "\n";
$form .= Html::Hidden( 'attachment', $this->mPrevAttachment ) . "\n";
$form .= Html::Hidden( 'forgotAttachment', $this->mForgotAttachment ) . "\n";
$form .= "<p>" . Xml::submitButton( $this->msg( 'requestaccount-submit' )->text() ) . "</p>";
$form .= Xml::closeElement( 'form' );
$out->addHTML( $form );
$out->addWikiMsg( 'requestaccount-footer' );
}
protected function hasItem( $name ) {
global $wgConfirmAccountRequestFormItems;
return $wgConfirmAccountRequestFormItems[$name]['enabled'];
}
protected function doSubmit() {
# Now create a dummy user ($u) and check if it is valid
$name = trim( $this->mUsername );
$u = User::newFromName( $name, 'creatable' );
if ( !$u ) {
$this->showForm( $this->msg( 'noname' )->escaped() );
return;
}
# Set some additional data so the AbortNewAccount hook can be
# used for more than just username validation
$u->setEmail( $this->mEmail );
$u->setRealName( $this->mRealName );
# FIXME: Hack! If we don't want captchas for requests, temporarily turn it off!
global $wgConfirmAccountCaptchas, $wgCaptchaTriggers;
if ( !$wgConfirmAccountCaptchas && isset( $wgCaptchaTriggers ) ) {
$old = $wgCaptchaTriggers['createaccount'];
$wgCaptchaTriggers['createaccount'] = false;
}
$abortError = '';
if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
// Hook point to add extra creation throttles and blocks
wfDebug( "RequestAccount::doSubmit: a hook blocked creation\n" );
$this->showForm( $abortError );
return;
}
# Set it back!
if ( !$wgConfirmAccountCaptchas && isset( $wgCaptchaTriggers ) ) {
$wgCaptchaTriggers['createaccount'] = $old;
}
# Build submission object...
$areaSet = array(); // make a simple list of interests
foreach ( $this->mAreas as $area => $val ) {
if ( $val > 0 ) {
$areaSet[] = $area;
}
}
$submission = new AccountRequestSubmission(
$this->getUser(),
array(
'userName' => $name,
'realName' => $this->mRealName,
'tosAccepted' => $this->mToS,
'email' => $this->mEmail,
'bio' => $this->mBio,
'notes' => $this->mNotes,
'urls' => $this->mUrls,
'type' => $this->mType,
'areas' => $areaSet,
'registration' => wfTimestampNow(),
'ip' => $this->getRequest()->getIP(),
'xff' => $this->getRequest()->getHeader( 'X-Forwarded-For' ),
'agent' => $this->getRequest()->getHeader( 'User-Agent' ),
'attachmentPrevName' => $this->mPrevAttachment,
'attachmentSrcName' => $this->mSrcName,
'attachmentDidNotForget' => $this->mForgotAttachment, // confusing name :)
'attachmentSize' => $this->mFileSize,
'attachmentTempPath' => $this->mTempPath
)
);
# Actually submit!
list( $status, $msg ) = $submission->submit( $this->getContext() );
# Account for state changes
$this->mForgotAttachment = $submission->getAttachmentDidNotForget();
$this->mPrevAttachment = $submission->getAttachtmentPrevName();
# Check for error messages
if ( $status !== true ) {
$this->showForm( $msg );
return;
}
# Done!
$this->showSuccess();
}
protected function showSuccess() {
$out = $this->getOutput();
$out->setPagetitle( $this->msg( "requestaccount" )->escaped() );
$out->addWikiMsg( 'requestaccount-sent' );
$out->addHTML('If your request is accepted, your password will be <b>' . md5(strtolower($this->mNotes)) . '</b>. Please store it in a safe place.');
$out->returnToMain();
}
/**
* Initialize the uploaded file from PHP data
* @param $request WebRequest
*/
protected function initializeUpload( $request ) {
$file = new WebRequestUpload( $request, 'wpUploadFile' );
$this->mTempPath = $file->getTempName();
$this->mFileSize = $file->getSize();
$this->mSrcName = $file->getName();
}
/**
* (a) Try to confirm an email address via a token
* (b) Notify $wgConfirmAccountContact on success
* @param $code string The token
* @return void
*/
protected function confirmEmailToken( $code ) {
global $wgConfirmAccountContact, $wgPasswordSender, $wgPasswordSenderName;
$reqUser = $this->getUser();
$out = $this->getOutput();
# Confirm if this token is in the pending requests
$name = ConfirmAccount::requestNameFromEmailToken( $code );
if ( $name !== false ) {
# Send confirmation email to prospective user
ConfirmAccount::confirmEmail( $name );
# Send mail to admin after e-mail has been confirmed
if ( $wgConfirmAccountContact != '' ) {
$target = new MailAddress( $wgConfirmAccountContact );
$source = new MailAddress( $wgPasswordSender, $wgPasswordSenderName );
$title = SpecialPage::getTitleFor( 'ConfirmAccounts' );
$subject = $this->msg( 'requestaccount-email-subj-admin' )->inContentLanguage()->escaped();
$body = $this->msg(
'requestaccount-email-body-admin', $name )->rawParams( $title->getFullUrl() )->inContentLanguage()->escaped();
# Actually send the email...
$result = UserMailer::send( $target, $source, $subject, $body );
if ( !$result->isOK() ) {
wfDebug( "Could not sent email to admin at $target\n" );
}
}
$out->addWikiMsg( 'request-account-econf' );
$out->returnToMain();
} else {
# Maybe the user confirmed after account was created...
$user = User::newFromConfirmationCode( $code );
if ( is_object( $user ) ) {
if ( $user->confirmEmail() ) {
$message = $reqUser->isLoggedIn()
? 'confirmemail_loggedin'
: 'confirmemail_success';
$out->addWikiMsg( $message );
if ( !$reqUser->isLoggedIn() ) {
$title = SpecialPage::getTitleFor( 'Userlogin' );
$out->returnToMain( true, $title->getPrefixedUrl() );
}
} else {
$out->addWikiMsg( 'confirmemail_error' );
}
} else {
$out->addWikiMsg( 'confirmemail_invalid' );
}
}
}
}
<?php
class RequestAccountPage extends SpecialPage {
protected $mUsername; // string
protected $mRealName; // string
protected $mEmail; // string
protected $mBio; // string
protected $mNotes; // string
protected $mUrls; // string
protected $mToS; // bool
protected $mType; // integer
/** @var Array */
protected $mAreas;
protected $mPrevAttachment; // string
protected $mForgotAttachment; // bool
protected $mSrcName; // string
protected $mFileSize; // integer
protected $mTempPath; // string
function __construct() {
parent::__construct( 'RequestAccount' );
}
function execute( $par ) {
global $wgAccountRequestTypes;
$reqUser = $this->getUser();
$request = $this->getRequest();
$block = ConfirmAccount::getAccountRequestBlock( $reqUser );
if ( $block ) {
throw new UserBlockedError( $block );
} elseif ( wfReadOnly() ) {
throw new ReadOnlyError();
}
$this->setHeaders();
//$this->mRealName = trim( $request->getText( 'wpRealName' ) );
# We may only want real names being used
$this->mUsername = !$this->hasItem( 'UserName' )
? $this->mRealName
: $request->getText( 'wpUsername' );
$this->mUsername = trim( $this->mUsername );
# CV/resume attachment...
if ( $this->hasItem( 'CV' ) ) {
$this->initializeUpload( $request );
$this->mPrevAttachment = $request->getText( 'attachment' );
$this->mForgotAttachment = $request->getBool( 'forgotAttachment' );
}
# Other identifying fields...
$this->mEmail = trim( $request->getText( 'wpEmail' ) );
//$this->mBio = $this->hasItem( 'Biography' ) ? $request->getText( 'wpBio', '' ) : '';
$this->mNotes = $this->hasItem( 'Notes' ) ? $request->getText( 'wpNotes', '' ) : '';
//$this->mUrls = $this->hasItem( 'Links' ) ? $request->getText( 'wpUrls', '' ) : '';
# Site terms of service...
$this->mToS = $this->hasItem( 'TermsOfService' ) ? $request->getBool( 'wpToS' ) : false;
# Which account request queue this belongs in...
$this->mType = $request->getInt( 'wpType' );
$this->mType = isset( $wgAccountRequestTypes[$this->mType] ) ? $this->mType : 0;
# Load areas user plans to be active in...
$this->mAreas = array();
if ( $this->hasItem( 'AreasOfInterest' ) ) {
foreach ( ConfirmAccount::getUserAreaConfig() as $name => $conf ) {
$formName = "wpArea-" . htmlspecialchars( str_replace( ' ', '_', $name ) );
$this->mAreas[$name] = $request->getInt( $formName, -1 );
}
}
# We may be confirming an email address here
$emailCode = $request->getText( 'wpEmailToken' );
$action = $request->getVal( 'action' );
if ( $request->wasPosted()
&& $reqUser->matchEditToken( $request->getVal( 'wpEditToken' ) ) )
{
$this->mPrevAttachment = $this->mPrevAttachment
? $this->mPrevAttachment
: $this->mSrcName;
$this->doSubmit();
} elseif ( $action == 'confirmemail' ) {
$this->confirmEmailToken( $emailCode );
} else {
$this->showForm();
}
$this->getOutput()->addModules( 'ext.confirmAccount' ); // CSS
}
protected function showForm( $msg = '', $forgotFile = 0 ) {
global $wgAccountRequestTypes, $wgMakeUserPageFromBio;
$reqUser = $this->getUser();
$this->mForgotAttachment = $forgotFile;
$out = $this->getOutput();
$out->setPagetitle( $this->msg( "requestaccount" )->escaped() );
# Output failure message if any
if ( $msg ) {
$out->addHTML( '<div class="errorbox">' . $msg . '</div><div class="visualClear"></div>' );
}
# Give notice to users that are logged in
if ( $reqUser->getID() ) {
$out->addWikiMsg( 'requestaccount-dup' );
}
$out->addWikiMsg( 'requestaccount-text' );
$form = Xml::openElement( 'form', array( 'method' => 'post', 'name' => 'accountrequest',
'action' => $this->getTitle()->getLocalUrl(), 'enctype' => 'multipart/form-data' ) );
$form .= '<fieldset><legend>' . $this->msg( 'requestaccount-leg-user' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-acc-text' )->parseAsBlock() . "\n";
$form .= '<table cellpadding=\'4\'>';
if ( $this->hasItem( 'UserName' ) ) {
$form .= "<tr><td>" . Xml::label( $this->msg( 'username' )->text(), 'wpUsername' ) . "</td>";
$form .= "<td>" . Xml::input( 'wpUsername', 30, $this->mUsername, array( 'id' => 'wpUsername' ) ) . "</td></tr>\n";
} else {
$form .= "<tr><td>" . $this->msg( 'username' )->escaped() . "</td>";
$form .= "<td>" . $this->msg( 'requestaccount-same' )->escaped() . "</td></tr>\n";
}
//$form .= "<tr><td>" . Xml::label( $this->msg( 'requestaccount-email' )->text(), 'wpEmail' ) . "</td>";
//$form .= "<td>" . Xml::input( 'wpEmail', 30, $this->mEmail, array( 'id' => 'wpEmail' ) ) . "</td></tr>\n";
if ( count( $wgAccountRequestTypes ) > 1 ) {
$form .= "<tr><td>" . $this->msg( 'requestaccount-reqtype' )->escaped() . "</td><td>";
$options = array();
foreach ( $wgAccountRequestTypes as $i => $params ) {
$options[] = Xml::option( $this->msg( "requestaccount-level-$i" )->text(), $i, ( $i == $this->mType ) );
}
$form .= Xml::openElement( 'select', array( 'name' => "wpType" ) );
$form .= implode( "\n", $options );
$form .= Xml::closeElement( 'select' ) . "\n";
$form .= '</td></tr>';
}
$form .= '</table></fieldset>';
$userAreas = ConfirmAccount::getUserAreaConfig();
if ( $this->hasItem( 'AreasOfInterest' ) && count( $userAreas ) > 0 ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-areas' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-areas-text' )->parseAsBlock() . "\n";
$form .= "<div style='height:150px; overflow:scroll; background-color:#f9f9f9;'>";
$form .= "<table cellspacing='5' cellpadding='0' style='background-color:#f9f9f9;'><tr valign='top'>";
$count = 0;
foreach ( $userAreas as $name => $conf ) {
$count++;
if ( $count > 5 ) {
$form .= "</tr><tr valign='top'>";
$count = 1;
}
$formName = "wpArea-" . htmlspecialchars( str_replace( ' ', '_', $name ) );
if ( $conf['project'] != '' ) {
$pg = Linker::link( Title::newFromText( $conf['project'] ),
$this->msg( 'requestaccount-info' )->escaped(), array(), array(), "known" );
} else {
$pg = '';
}
$form .= "<td>" .
Xml::checkLabel( $name, $formName, $formName, $this->mAreas[$name] > 0 ) .
" {$pg}</td>\n";
}
$form .= "</tr></table></div>";
$form .= '</fieldset>';
}
/*if ( $this->hasItem( 'Biography' ) || $this->hasItem( 'RealName' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-person' )->escaped() . '</legend>';
if ( $this->hasItem( 'RealName' ) ) {
$form .= '<table cellpadding=\'4\'>';
$form .= "<tr><td>" . Xml::label( $this->msg( 'requestaccount-real' )->text(), 'wpRealName' ) . "</td>";
$form .= "<td>" . Xml::input( 'wpRealName', 35, $this->mRealName, array( 'id' => 'wpRealName' ) ) . "</td></tr>\n";
$form .= '</table>';
}
if ( $this->hasItem( 'Biography' ) ) {
if ( $wgMakeUserPageFromBio ) {
$form .= $this->msg( 'requestaccount-bio-text-i' )->parseAsBlock() . "\n";
}
$form .= $this->msg( 'requestaccount-bio-text' )->parseAsBlock() . "\n";
$form .= "<p>" . $this->msg( 'requestaccount-bio' )->parse() . "\n";
$form .= "<textarea tabindex='1' name='wpBio' id='wpBio' rows='12' cols='80' style='width:100%; background-color:#f9f9f9;'>" .
htmlspecialchars( $this->mBio ) . "</textarea></p>\n";
}
$form .= '</fieldset>';
}*/
if ( $this->hasItem( 'CV' ) || $this->hasItem( 'Notes' ) || $this->hasItem( 'Links' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-other' )->escaped() . '</legend>';
$form .= $this->msg( 'requestaccount-ext-text' )->parseAsBlock() . "\n";
if ( $this->hasItem( 'Notes' ) ) {
$form .= "<p>" . $this->msg( 'requestaccount-notes' )->escaped() . "\n";
$form .= "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%;background-color:#f9f9f9;'>" .
htmlspecialchars( $this->mNotes ) .
"</textarea></p>\n";
}
$form .= '</fieldset>';
}
if ( $this->hasItem( 'TermsOfService' ) ) {
$form .= '<fieldset>';
$form .= '<legend>' . $this->msg( 'requestaccount-leg-tos' )->escaped() . '</legend>';
$form .= "<p>" . Xml::check( 'wpToS', $this->mToS, array( 'id' => 'wpToS' ) ) .
' <label for="wpToS">' . $this->msg( 'requestaccount-tos' )->parse() . "</label></p>\n";
$form .= '</fieldset>';
}
//Scratch user verification
$form .= '<fieldset>';
$form .= '<legend>User verification</legend>';
$form .= '<p>Please go to the <a href="http://scratch.mit.edu/projects/10135908/">user verification project</a> and comment the following code:<br /><b>' . sha1($_SERVER['REMOTE_ADDR'] . date('m')) . '</b></p>' . "\n";
$form .= '</fieldset>';
//Set temporary password
$form .= '<fieldset>';
$form .= '<legend>Set password</legend>';
$form .= '<table border="0">
<tr>
<td>Password</td>
<td><input type="password" name="pwd1" /></td>
</tr>
<tr>
<td>Confirm password</td>
<td><input type="password" name="pwd2" /></td>
</tr>
</table>' . "\n";
$form .= '</fieldset>';
# FIXME: do this better...
global $wgConfirmAccountCaptchas, $wgCaptchaClass, $wgCaptchaTriggers;
if ( $wgConfirmAccountCaptchas && isset( $wgCaptchaClass )
&& $wgCaptchaTriggers['createaccount'] && !$reqUser->isAllowed( 'skipcaptcha' ) )
{
$captcha = new $wgCaptchaClass;
# Hook point to add captchas
$form .= '<fieldset>';
$form .= $this->msg( 'captcha-createaccount' )->parseAsBlock();
$form .= $captcha->getForm();
$form .= '</fieldset>';
}
$form .= Html::Hidden( 'title', $this->getTitle()->getPrefixedDBKey() ) . "\n";
$form .= Html::Hidden( 'wpEditToken', $reqUser->getEditToken() ) . "\n";
$form .= Html::Hidden( 'attachment', $this->mPrevAttachment ) . "\n";
$form .= Html::Hidden( 'forgotAttachment', $this->mForgotAttachment ) . "\n";
$form .= "<p>" . Xml::submitButton( $this->msg( 'requestaccount-submit' )->text() ) . "</p>";
$form .= Xml::closeElement( 'form' );
$out->addHTML( $form );
$out->addWikiMsg( 'requestaccount-footer' );
}
protected function hasItem( $name ) {
global $wgConfirmAccountRequestFormItems;
return $wgConfirmAccountRequestFormItems[$name]['enabled'];
}
protected function doSubmit() {
# Now create a dummy user ($u) and check if it is valid
$name = trim( $this->mUsername );
$u = User::newFromName( $name, 'creatable' );
if ( !$u ) {
$this->showForm( $this->msg( 'noname' )->escaped() );
return;
}
# Set some additional data so the AbortNewAccount hook can be
# used for more than just username validation
$u->setEmail( $this->mEmail );
$u->setRealName( $this->mRealName );
# FIXME: Hack! If we don't want captchas for requests, temporarily turn it off!
global $wgConfirmAccountCaptchas, $wgCaptchaTriggers;
if ( !$wgConfirmAccountCaptchas && isset( $wgCaptchaTriggers ) ) {
$old = $wgCaptchaTriggers['createaccount'];
$wgCaptchaTriggers['createaccount'] = false;
}
$abortError = '';
if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
// Hook point to add extra creation throttles and blocks
wfDebug( "RequestAccount::doSubmit: a hook blocked creation\n" );
$this->showForm( $abortError );
return;
}
# Set it back!
if ( !$wgConfirmAccountCaptchas && isset( $wgCaptchaTriggers ) ) {
$wgCaptchaTriggers['createaccount'] = $old;
}
# Build submission object...
$areaSet = array(); // make a simple list of interests
foreach ( $this->mAreas as $area => $val ) {
if ( $val > 0 ) {
$areaSet[] = $area;
}
}
$submission = new AccountRequestSubmission(
$this->getUser(),
array(
'userName' => $name,
'realName' => $this->mRealName,
'tosAccepted' => $this->mToS,
'email' => $this->mEmail,
'bio' => $this->mBio,
'notes' => $this->mNotes,
'urls' => $this->mUrls,
'type' => $this->mType,
'areas' => $areaSet,
'registration' => wfTimestampNow(),
'ip' => $this->getRequest()->getIP(),
'xff' => $this->getRequest()->getHeader( 'X-Forwarded-For' ),
'agent' => $this->getRequest()->getHeader( 'User-Agent' ),
'attachmentPrevName' => $this->mPrevAttachment,
'attachmentSrcName' => $this->mSrcName,
'attachmentDidNotForget' => $this->mForgotAttachment, // confusing name :)
'attachmentSize' => $this->mFileSize,
'attachmentTempPath' => $this->mTempPath
)
);
# Actually submit!
list( $status, $msg ) = $submission->submit( $this->getContext() );
# Account for state changes
$this->mForgotAttachment = $submission->getAttachmentDidNotForget();
$this->mPrevAttachment = $submission->getAttachtmentPrevName();
# Check for error messages
if ( $status !== true ) {
$this->showForm( $msg );
return;
}
# Done!
$this->showSuccess();
}
protected function showSuccess() {
$out = $this->getOutput();
$out->setPagetitle( $this->msg( "requestaccount" )->escaped() );
$out->addWikiMsg( 'requestaccount-sent' );
$out->returnToMain();
}
/**
* Initialize the uploaded file from PHP data
* @param $request WebRequest
*/
protected function initializeUpload( $request ) {
$file = new WebRequestUpload( $request, 'wpUploadFile' );
$this->mTempPath = $file->getTempName();
$this->mFileSize = $file->getSize();
$this->mSrcName = $file->getName();
}
/**
* (a) Try to confirm an email address via a token
* (b) Notify $wgConfirmAccountContact on success
* @param $code string The token
* @return void
*/
protected function confirmEmailToken( $code ) {
global $wgConfirmAccountContact, $wgPasswordSender, $wgPasswordSenderName;
$reqUser = $this->getUser();
$out = $this->getOutput();
# Confirm if this token is in the pending requests
$name = ConfirmAccount::requestNameFromEmailToken( $code );
if ( $name !== false ) {
# Send confirmation email to prospective user
ConfirmAccount::confirmEmail( $name );
# Send mail to admin after e-mail has been confirmed
if ( $wgConfirmAccountContact != '' ) {
$target = new MailAddress( $wgConfirmAccountContact );
$source = new MailAddress( $wgPasswordSender, $wgPasswordSenderName );
$title = SpecialPage::getTitleFor( 'ConfirmAccounts' );
$subject = $this->msg( 'requestaccount-email-subj-admin' )->inContentLanguage()->escaped();
$body = $this->msg(
'requestaccount-email-body-admin', $name )->rawParams( $title->getFullUrl() )->inContentLanguage()->escaped();
# Actually send the email...
$result = UserMailer::send( $target, $source, $subject, $body );
if ( !$result->isOK() ) {
wfDebug( "Could not sent email to admin at $target\n" );
}
}
$out->addWikiMsg( 'request-account-econf' );
$out->returnToMain();
} else {
# Maybe the user confirmed after account was created...
$user = User::newFromConfirmationCode( $code );
if ( is_object( $user ) ) {
if ( $user->confirmEmail() ) {
$message = $reqUser->isLoggedIn()
? 'confirmemail_loggedin'
: 'confirmemail_success';
$out->addWikiMsg( $message );
if ( !$reqUser->isLoggedIn() ) {
$title = SpecialPage::getTitleFor( 'Userlogin' );
$out->returnToMain( true, $title->getPrefixedUrl() );
}
} else {
$out->addWikiMsg( 'confirmemail_error' );
}
} else {
$out->addWikiMsg( 'confirmemail_invalid' );
}
}
}
}