diff --git a/ConfirmAccount/README.txt b/ConfirmAccount/README.txt index c0168f2..c333410 100644 --- a/ConfirmAccount/README.txt +++ b/ConfirmAccount/README.txt @@ -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. \ No newline at end of file +© GPL, Aaron Schulz diff --git a/ConfirmAccount/business/AccountConfirmSubmission.php b/ConfirmAccount/business/AccountConfirmSubmission.php index 07b8797..16226ef 100644 --- a/ConfirmAccount/business/AccountConfirmSubmission.php +++ b/ConfirmAccount/business/AccountConfirmSubmission.php @@ -1,450 +1,451 @@ -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 - ); - } - } -} +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 + ); + } + } +} diff --git a/ConfirmAccount/business/AccountRequestSubmission.php b/ConfirmAccount/business/AccountRequestSubmission.php index a406f50..376c7f1 100644 --- a/ConfirmAccount/business/AccountRequestSubmission.php +++ b/ConfirmAccount/business/AccountRequestSubmission.php @@ -1,297 +1,301 @@ -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('%
.*?.*?
(.*?)
%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 ); - } -} +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('%
.*?.*?
(.*?)
%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 ); + } +} diff --git a/ConfirmAccount/frontend/specialpages/actions/ConfirmAccount_body.php b/ConfirmAccount/frontend/specialpages/actions/ConfirmAccount_body.php index bceadc8..0a9441f 100644 --- a/ConfirmAccount/frontend/specialpages/actions/ConfirmAccount_body.php +++ b/ConfirmAccount/frontend/specialpages/actions/ConfirmAccount_body.php @@ -379,9 +379,11 @@ class ConfirmAccountsPage extends SpecialPage { } if ( $this->hasItem( 'Notes' ) ) { $form .= "

" . $this->msg( 'confirmaccount-notes' )->escaped() . "\n"; - $form .= "

\n"; + $form .= "

\n"; } if ( $this->hasItem( 'Links' ) ) { $form .= "

" . $this->msg( 'confirmaccount-urls' )->escaped() . "

\n"; diff --git a/ConfirmAccount/frontend/specialpages/actions/RequestAccount_body.php b/ConfirmAccount/frontend/specialpages/actions/RequestAccount_body.php index 84a82a9..20be5e0 100644 --- a/ConfirmAccount/frontend/specialpages/actions/RequestAccount_body.php +++ b/ConfirmAccount/frontend/specialpages/actions/RequestAccount_body.php @@ -1,397 +1,411 @@ -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( '
' . $msg . '
' ); - } - # 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 .= '
' . $this->msg( 'requestaccount-leg-user' )->escaped() . ''; - $form .= $this->msg( 'requestaccount-acc-text' )->parseAsBlock() . "\n"; - $form .= ''; - if ( $this->hasItem( 'UserName' ) ) { - $form .= ""; - $form .= "\n"; - } else { - $form .= ""; - $form .= "\n"; - } - //$form .= ""; - //$form .= "\n"; - if ( count( $wgAccountRequestTypes ) > 1 ) { - $form .= "'; - } - $form .= '
" . Xml::label( $this->msg( 'username' )->text(), 'wpUsername' ) . "" . Xml::input( 'wpUsername', 30, $this->mUsername, array( 'id' => 'wpUsername' ) ) . "
" . $this->msg( 'username' )->escaped() . "" . $this->msg( 'requestaccount-same' )->escaped() . "
" . Xml::label( $this->msg( 'requestaccount-email' )->text(), 'wpEmail' ) . "" . Xml::input( 'wpEmail', 30, $this->mEmail, array( 'id' => 'wpEmail' ) ) . "
" . $this->msg( 'requestaccount-reqtype' )->escaped() . ""; - $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 .= '
'; - - $userAreas = ConfirmAccount::getUserAreaConfig(); - if ( $this->hasItem( 'AreasOfInterest' ) && count( $userAreas ) > 0 ) { - $form .= '
'; - $form .= '' . $this->msg( 'requestaccount-leg-areas' )->escaped() . ''; - $form .= $this->msg( 'requestaccount-areas-text' )->parseAsBlock() . "\n"; - - $form .= "
"; - $form .= ""; - $count = 0; - foreach ( $userAreas as $name => $conf ) { - $count++; - if ( $count > 5 ) { - $form .= ""; - $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 .= "\n"; - } - $form .= "
" . - Xml::checkLabel( $name, $formName, $formName, $this->mAreas[$name] > 0 ) . - " {$pg}
"; - $form .= '
'; - } - - /*if ( $this->hasItem( 'Biography' ) || $this->hasItem( 'RealName' ) ) { - $form .= '
'; - $form .= '' . $this->msg( 'requestaccount-leg-person' )->escaped() . ''; - if ( $this->hasItem( 'RealName' ) ) { - $form .= ''; - $form .= ""; - $form .= "\n"; - $form .= '
" . Xml::label( $this->msg( 'requestaccount-real' )->text(), 'wpRealName' ) . "" . Xml::input( 'wpRealName', 35, $this->mRealName, array( 'id' => 'wpRealName' ) ) . "
'; - } - if ( $this->hasItem( 'Biography' ) ) { - if ( $wgMakeUserPageFromBio ) { - $form .= $this->msg( 'requestaccount-bio-text-i' )->parseAsBlock() . "\n"; - } - $form .= $this->msg( 'requestaccount-bio-text' )->parseAsBlock() . "\n"; - $form .= "

" . $this->msg( 'requestaccount-bio' )->parse() . "\n"; - $form .= "

\n"; - } - $form .= '
'; - }*/ - - if ( $this->hasItem( 'CV' ) || $this->hasItem( 'Notes' ) || $this->hasItem( 'Links' ) ) { - $form .= '
'; - $form .= '' . $this->msg( 'requestaccount-leg-other' )->escaped() . ''; - $form .= $this->msg( 'requestaccount-ext-text' )->parseAsBlock() . "\n"; - if ( $this->hasItem( 'Notes' ) ) { - $form .= "

" . $this->msg( 'requestaccount-notes' )->escaped() . "\n"; - $form .= "

\n"; - } - $form .= '
'; - } - - if ( $this->hasItem( 'TermsOfService' ) ) { - $form .= '
'; - $form .= '' . $this->msg( 'requestaccount-leg-tos' )->escaped() . ''; - $form .= "

" . Xml::check( 'wpToS', $this->mToS, array( 'id' => 'wpToS' ) ) . - '

\n"; - $form .= '
'; - } - - //Scratch user verification - $form .= '
'; - $form .= 'User verification'; - $form .= '

Please go to the user verification project and comment the following code:
' . sha1($_SERVER['REMOTE_ADDR'] . date('m')) . '

' . "\n"; - $form .= '
'; - - # 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 .= '
'; - $form .= $this->msg( 'captcha-createaccount' )->parseAsBlock(); - $form .= $captcha->getForm(); - $form .= '
'; - } - $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 .= "

" . Xml::submitButton( $this->msg( 'requestaccount-submit' )->text() ) . "

"; - $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 ' . md5(strtolower($this->mNotes)) . '. 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' ); - } - } - } -} +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( '
' . $msg . '
' ); + } + # 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 .= '
' . $this->msg( 'requestaccount-leg-user' )->escaped() . ''; + $form .= $this->msg( 'requestaccount-acc-text' )->parseAsBlock() . "\n"; + $form .= ''; + if ( $this->hasItem( 'UserName' ) ) { + $form .= ""; + $form .= "\n"; + } else { + $form .= ""; + $form .= "\n"; + } + //$form .= ""; + //$form .= "\n"; + if ( count( $wgAccountRequestTypes ) > 1 ) { + $form .= "'; + } + $form .= '
" . Xml::label( $this->msg( 'username' )->text(), 'wpUsername' ) . "" . Xml::input( 'wpUsername', 30, $this->mUsername, array( 'id' => 'wpUsername' ) ) . "
" . $this->msg( 'username' )->escaped() . "" . $this->msg( 'requestaccount-same' )->escaped() . "
" . Xml::label( $this->msg( 'requestaccount-email' )->text(), 'wpEmail' ) . "" . Xml::input( 'wpEmail', 30, $this->mEmail, array( 'id' => 'wpEmail' ) ) . "
" . $this->msg( 'requestaccount-reqtype' )->escaped() . ""; + $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 .= '
'; + + $userAreas = ConfirmAccount::getUserAreaConfig(); + if ( $this->hasItem( 'AreasOfInterest' ) && count( $userAreas ) > 0 ) { + $form .= '
'; + $form .= '' . $this->msg( 'requestaccount-leg-areas' )->escaped() . ''; + $form .= $this->msg( 'requestaccount-areas-text' )->parseAsBlock() . "\n"; + + $form .= "
"; + $form .= ""; + $count = 0; + foreach ( $userAreas as $name => $conf ) { + $count++; + if ( $count > 5 ) { + $form .= ""; + $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 .= "\n"; + } + $form .= "
" . + Xml::checkLabel( $name, $formName, $formName, $this->mAreas[$name] > 0 ) . + " {$pg}
"; + $form .= '
'; + } + + /*if ( $this->hasItem( 'Biography' ) || $this->hasItem( 'RealName' ) ) { + $form .= '
'; + $form .= '' . $this->msg( 'requestaccount-leg-person' )->escaped() . ''; + if ( $this->hasItem( 'RealName' ) ) { + $form .= ''; + $form .= ""; + $form .= "\n"; + $form .= '
" . Xml::label( $this->msg( 'requestaccount-real' )->text(), 'wpRealName' ) . "" . Xml::input( 'wpRealName', 35, $this->mRealName, array( 'id' => 'wpRealName' ) ) . "
'; + } + if ( $this->hasItem( 'Biography' ) ) { + if ( $wgMakeUserPageFromBio ) { + $form .= $this->msg( 'requestaccount-bio-text-i' )->parseAsBlock() . "\n"; + } + $form .= $this->msg( 'requestaccount-bio-text' )->parseAsBlock() . "\n"; + $form .= "

" . $this->msg( 'requestaccount-bio' )->parse() . "\n"; + $form .= "

\n"; + } + $form .= '
'; + }*/ + + if ( $this->hasItem( 'CV' ) || $this->hasItem( 'Notes' ) || $this->hasItem( 'Links' ) ) { + $form .= '
'; + $form .= '' . $this->msg( 'requestaccount-leg-other' )->escaped() . ''; + $form .= $this->msg( 'requestaccount-ext-text' )->parseAsBlock() . "\n"; + if ( $this->hasItem( 'Notes' ) ) { + $form .= "

" . $this->msg( 'requestaccount-notes' )->escaped() . "\n"; + $form .= "

\n"; + } + $form .= '
'; + } + + if ( $this->hasItem( 'TermsOfService' ) ) { + $form .= '
'; + $form .= '' . $this->msg( 'requestaccount-leg-tos' )->escaped() . ''; + $form .= "

" . Xml::check( 'wpToS', $this->mToS, array( 'id' => 'wpToS' ) ) . + '

\n"; + $form .= '
'; + } + + //Scratch user verification + $form .= '
'; + $form .= 'User verification'; + $form .= '

Please go to the user verification project and comment the following code:
' . sha1($_SERVER['REMOTE_ADDR'] . date('m')) . '

' . "\n"; + $form .= '
'; + + //Set temporary password + $form .= '
'; + $form .= 'Set password'; + $form .= ' + + + + + + + + +
Password
Confirm password
' . "\n"; + $form .= '
'; + + # 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 .= '
'; + $form .= $this->msg( 'captcha-createaccount' )->parseAsBlock(); + $form .= $captcha->getForm(); + $form .= '
'; + } + $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 .= "

" . Xml::submitButton( $this->msg( 'requestaccount-submit' )->text() ) . "

"; + $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' ); + } + } + } +}