Version 4.2.0

This commit is contained in:
Neo committed 2025-12-19 05:44:59 -08:00
1 parent 0dc2aee5ed
commit 96997ddd8e
2074 files changed
+125454 -55780

No files matched your search

@@ -1,12 +1,11 @@
<?php
/**
* @brief Register
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 3 July 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\core\modules\front\system;
@@ -58,7 +57,31 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Output::i()->showOffline();
}
if( isset( $_SESSION['coppa_user'] ) )
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=register', NULL, 'register' ), array(), 'loc_registering' );
\IPS\Output::i()->allowDefaultWidgets = FALSE;
/* Quick registration does not work with COPPA */
if ( \IPS\Settings::i()->quick_register )
{
$form = $this->_registrationForm();
$form->class = 'ipsForm_fullWidth';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('registration');
if( (string) \IPS\Request::i()->url() == (string) \IPS\Http\Url::internal( 'app=core&module=system&controller=register', NULL, 'register' ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->register( $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupRegisterTemplate' ), new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login', NULL, \IPS\Settings::i()->logins_over_https ) ) ), new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login', NULL, \IPS\Settings::i()->logins_over_https ) ) );
}
else
{
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupRegisterTemplate' ), new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login', NULL, \IPS\Settings::i()->logins_over_https ) ) );
}
return;
}
if( isset( $_SESSION['coppa_user'] ) AND ( \IPS\Settings::i()->use_coppa OR \IPS\Settings::i()->minimum_age > 0 ) )
{
if ( \IPS\Settings::i()->minimum_age > 0 )
{
@@ -112,14 +135,30 @@ class _register extends \IPS\Dispatcher\Controller
$steps['basic_info'] = function ( $data ) use ( $self )
{
$form = \IPS\core\modules\front\system\register::buildRegistrationForm();
$form = $this->_registrationForm();
return \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->register( $form, new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login', NULL, \IPS\Settings::i()->logins_over_https ) ) );
};
/* Output */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('registration');
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard( $steps, \IPS\Http\Url::internal( 'app=core&module=system&controller=register' ), FALSE );
}
/**
* Normal registration form
*
* @return \IPS\Form
*/
protected function _registrationForm()
{
$form = \IPS\core\modules\front\system\register::buildRegistrationForm();
/* Handle submissions */
if ( $values = $form->values() )
/* Handle submissions */
if ( $values = $form->values() )
{
if( !\IPS\Settings::i()->quick_register )
{
/* Custom Fields */
$profileFields = array();
foreach ( \IPS\core\ProfileFields\Field::fields( array(), \IPS\core\ProfileFields\Field::REG ) as $group => $fields )
{
foreach ( $fields as $id => $field )
@@ -133,63 +172,111 @@ class _register extends \IPS\Dispatcher\Controller
if ( $fields instanceof \IPS\Helpers\Form\Editor )
{
$field->claimAttachments( $self->id );
$field->claimAttachments( $this->id );
}
}
}
if ( \IPS\Settings::i()->security_questions_enabled and ( \IPS\Settings::i()->security_questions_prompt === 'register' or ( \IPS\Settings::i()->security_questions_prompt === 'optional' and !$values['security_questions_optout_title'] ) ) )
{
$answers = array();
foreach ( $values as $k => $v )
{
if ( preg_match( '/^security_question_q_(\d+)$/', $k, $matches ) )
{
if ( isset( $answers[ $v ] ) )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'security_questions_unique', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->security_questions_number ?: 3 ) ) );
break;
}
else
{
$answers[ $v ] = $v;
}
}
}
}
if ( !$form->error )
{
/* Create Member */
$member = \IPS\core\modules\front\system\register::_createMember( $values, $profileFields );
/* Log them in */
\IPS\Session::i()->setMember( $member );
$redirectUrl = \IPS\Login::getRegistrationDestination( $member );
/* Redirect */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'redirect' => (string) $redirectUrl ) );
}
else
{
\IPS\Output::i()->redirect( $redirectUrl );
}
}
}
else
{
$profileFields = array();
}
if ( \IPS\Settings::i()->security_questions_enabled and ( \IPS\Settings::i()->security_questions_prompt === 'register' or ( \IPS\Settings::i()->security_questions_prompt === 'optional' and !$values['security_questions_optout_title'] ) ) )
{
$answers = array();
foreach ( $values as $k => $v )
{
if ( preg_match( '/^security_question_q_(\d+)$/', $k, $matches ) )
{
if ( isset( $answers[ $v ] ) )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'security_questions_unique', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->security_questions_number ?: 3 ) ) );
break;
}
else
{
$answers[ $v ] = $v;
}
}
}
}
if ( !$form->error )
{
/* Create Member */
$member = \IPS\core\modules\front\system\register::_createMember( $values, $profileFields );
return \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->register( $form, new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login', NULL, \IPS\Settings::i()->logins_over_https ) ) );
};
/* Log them in */
\IPS\Session::i()->setMember( $member );
\IPS\Member\Device::loadOrCreate( $member, FALSE )->updateAfterAuthentication( TRUE, '' );
$redirectUrl = \IPS\Login::getRegistrationDestination( $member );
/* Redirect */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'redirect' => (string) $redirectUrl ) );
}
else
{
\IPS\Output::i()->redirect( $redirectUrl );
}
}
}
/* Set Session Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=register', NULL, 'register' ), array(), 'loc_registering' );
\IPS\Output::i()->allowDefaultWidgets = FALSE;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('registration');
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard( $steps, \IPS\Http\Url::internal( 'app=core&module=system&controller=register' ), FALSE );
return $form;
}
/**
* Finish
*
* @return void
*/
public function finish()
{
$steps = \IPS\Member\ProfileStep::loadAll();
/* Do we need to bother? We should only show this form if there are required items, but will show both required and suggested where possible to allow the user to complete as much of their profile as possible */
$haveRequired = FALSE;
foreach( $steps AS $id => $step )
{
if ( $step->required )
{
$haveRequired = TRUE;
break;
}
}
if ( $haveRequired === FALSE )
{
/* Nope, forward */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
}
$wizardSteps = array();
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=finish', 'front', 'register' );
foreach( \IPS\Application::allExtensions( 'core', 'ProfileSteps' ) AS $extension )
{
if ( method_exists( $extension, 'wizard') AND count( $extension::wizard() ) )
{
$wizardSteps = array_merge( $wizardSteps, $extension::wizard() );
}
}
$wizardSteps = array_merge( $wizardSteps, array( 'profile_done' => function( $data ) {
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ), 'saved' );
} ) );
$wizard = new \IPS\Helpers\Wizard( $wizardSteps, $url, TRUE, NULL, TRUE );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( '2fa.css', 'core', 'global' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/settings.css' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('complete_profile_registration');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->finishRegistration( $wizard );
}
/**
* Build Registration Form
*
@@ -205,14 +292,21 @@ class _register extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', NULL, TRUE, array( 'confirm' => 'password' ) ) );
/* Profile fields */
foreach ( \IPS\core\ProfileFields\Field::fields( array(), \IPS\core\ProfileFields\Field::REG ) as $group => $fields )
if ( ! \IPS\Settings::i()->quick_register )
{
foreach ( $fields as $field )
foreach ( \IPS\core\ProfileFields\Field::fields( array(), \IPS\core\ProfileFields\Field::REG ) as $group => $fields )
{
$form->add( $field );
foreach ( $fields as $field )
{
$form->add( $field );
}
}
$form->addSeparator();
}
else
{
$form->class = 'ipsForm_vertical';
}
$form->addSeparator();
$question = FALSE;
try
@@ -221,53 +315,8 @@ class _register extends \IPS\Dispatcher\Controller
}
catch ( \UnderflowException $e ) {}
/* Security Questions */
if ( \IPS\Settings::i()->security_questions_enabled and in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
{
$numberOfQuestions = \IPS\Settings::i()->security_questions_number ?: 3;
$securityQuestions = array();
foreach ( \IPS\MFA\SecurityQuestions\Question::roots() as $securityQuestion )
{
$securityQuestions[ $securityQuestion->id ] = $securityQuestion->_title;
}
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('security_questions_setup_blurb', FALSE, array( 'pluralize' => array( $numberOfQuestions ) ) ) );
if ( \IPS\Settings::i()->security_questions_prompt === 'optional' )
{
$securityOptoutToggles = array();
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
{
$securityOptoutToggles[] = 'security_question_q_' . $i;
$securityOptoutToggles[] = 'security_question_a_' . $i;
}
$optOutCheckbox = new \IPS\Helpers\Form\Checkbox( 'security_questions_optout_title', FALSE, FALSE, array( 'togglesOff' => $securityOptoutToggles ) );
if ( \IPS\Member::loggedIn()->language()->checkKeyExists('security_questions_opt_out_warning_value') )
{
$optOutCheckbox->description = \IPS\Member::loggedIn()->language()->addToStack('security_questions_opt_out_warning_value');
}
$form->add( $optOutCheckbox );
}
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
{
$securityValidation = function( $val ) {
if ( !$val and ( \IPS\Settings::i()->security_questions_prompt === 'register' or !isset( \IPS\Request::i()->security_questions_optout_title_checkbox ) ) )
{
throw new \DomainException('form_required');
}
};
$questionField = new \IPS\Helpers\Form\Select( 'security_question_q_' . $i, NULL, FALSE, array( 'options' => $securityQuestions ), $securityValidation, NULL, NULL, 'security_question_q_' . $i );
$questionField->label = \IPS\Member::loggedIn()->language()->addToStack('security_question_q');
$answerField = new \IPS\Helpers\Form\Text( 'security_question_a_' . $i, NULL, NULL, array(), $securityValidation, NULL, NULL, 'security_question_a_' . $i );
$answerField->label = \IPS\Member::loggedIn()->language()->addToStack('security_question_a');
$form->add( $questionField );
$form->add( $answerField );
}
$form->addSeparator();
}
/* 2FA Q&A? */
static::addQuestion2FA( $form );
/* Random Q&A */
if( $question )
@@ -354,15 +403,74 @@ class _register extends \IPS\Dispatcher\Controller
} ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_system.js', 'core', 'front' ), \IPS\Output::i()->js( 'front_templates.js', 'core', 'front' ) );
return $form;
}
/**
* Add in the Q&A 2FA if it is enforced
*
* @param \IPS\Form $form Form
* @return void
*/
protected static function addQuestion2FA( &$form )
{
/* Security Questions */
if ( \IPS\Settings::i()->security_questions_enabled and in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
{
$numberOfQuestions = \IPS\Settings::i()->security_questions_number ?: 3;
$securityQuestions = array();
foreach ( \IPS\MFA\SecurityQuestions\Question::roots() as $securityQuestion )
{
$securityQuestions[ $securityQuestion->id ] = $securityQuestion->_title;
}
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('security_questions_setup_blurb', FALSE, array( 'pluralize' => array( $numberOfQuestions ) ) ) );
if ( \IPS\Settings::i()->security_questions_prompt === 'optional' )
{
$securityOptoutToggles = array();
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
{
$securityOptoutToggles[] = 'security_question_q_' . $i;
$securityOptoutToggles[] = 'security_question_a_' . $i;
}
$optOutCheckbox = new \IPS\Helpers\Form\Checkbox( 'security_questions_optout_title', FALSE, FALSE, array( 'togglesOff' => $securityOptoutToggles ) );
if ( \IPS\Member::loggedIn()->language()->checkKeyExists('security_questions_opt_out_warning_value') )
{
$optOutCheckbox->description = \IPS\Member::loggedIn()->language()->addToStack('security_questions_opt_out_warning_value', TRUE, array( 'returnBlank' => TRUE ) );
}
$form->add( $optOutCheckbox );
}
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
{
$securityValidation = function( $val ) {
if ( !$val and ( \IPS\Settings::i()->security_questions_prompt === 'register' or !isset( \IPS\Request::i()->security_questions_optout_title_checkbox ) ) )
{
throw new \DomainException('form_required');
}
};
$questionField = new \IPS\Helpers\Form\Select( 'security_question_q_' . $i, NULL, FALSE, array( 'options' => $securityQuestions ), $securityValidation, NULL, NULL, 'security_question_q_' . $i );
$questionField->label = \IPS\Member::loggedIn()->language()->addToStack('security_question_q');
$answerField = new \IPS\Helpers\Form\Text( 'security_question_a_' . $i, NULL, NULL, array(), $securityValidation, NULL, NULL, 'security_question_a_' . $i );
$answerField->label = \IPS\Member::loggedIn()->language()->addToStack('security_question_a');
$form->add( $questionField );
$form->add( $answerField );
}
$form->addSeparator();
}
}
/**
* Create Member
*
* @param array $values Values from form
* @param array $profileFields Profile field values from registration
* @return \IPS\Member
* @param array $values Values from form
* @param array $profileFields Profile field values from registration
* @return \IPS\Member
*/
public static function _createMember( $values, $profileFields )
{
@@ -385,15 +493,28 @@ class _register extends \IPS\Dispatcher\Controller
}
}
/* Save */
/* Initial Save */
$member->save();
/* This looks a bit weird, but the extensions expect an account to exist at this point, so we'll let the system save it now, then do what we need to do, then save again */
foreach( \IPS\Member\ProfileStep::loadAll() AS $step )
{
$extension = $step->extension;
$extension::formatFormValues( $values, $member, $form );
}
/* Save anything the profile extensions did */
$member->save();
/* Security Questions */
if ( \IPS\Settings::i()->security_questions_enabled and in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
{
if ( $values['security_questions_optout_title'] )
if ( isset( $values['security_questions_optout_title'] ) )
{
$member->members_bitoptions['security_questions_opt_out'] = TRUE;
/* Log MFA Opt-out */
$member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => TRUE ) );
}
else
{
@@ -417,12 +538,18 @@ class _register extends \IPS\Dispatcher\Controller
}
$member->members_bitoptions['has_security_answers'] = TRUE;
/* Log MFA Enable */
$member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => TRUE ) );
}
$member->save();
}
/* Save custom field values */
\IPS\Db::i()->replace( 'core_pfields_content', array_merge( array( 'member_id' => $member->member_id ), $profileFields ) );
if ( ! \IPS\Settings::i()->quick_register )
{
\IPS\Db::i()->replace( 'core_pfields_content', array_merge( array( 'member_id' => $member->member_id ), $profileFields ) );
}
/* Handle validation */
$member->postRegistration();
@@ -548,6 +675,7 @@ class _register extends \IPS\Dispatcher\Controller
/* Log in and Redirect */
\IPS\Session::i()->setMember( $member );
\IPS\Member\Device::loadOrCreate( $member )->updateAfterAuthentication( TRUE );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ), 'validate_email_confirmation' );
}
}
@@ -678,6 +806,13 @@ class _register extends \IPS\Dispatcher\Controller
}
\IPS\Member::loggedIn()->save();
/* Invalidate sessions except this one */
\IPS\Member::loggedIn()->invalidateSessionsAndLogins( \IPS\Session::i()->id );
if( isset( \IPS\Request::i()->cookie['login_key'] ) )
{
\IPS\Member\Device::loadOrCreate( \IPS\Member::loggedIn() )->updateAfterAuthentication( TRUE );
}
/* If email validation is required, do that... */
if ( in_array( \IPS\Settings::i()->reg_auth_type, array( 'user', 'admin_user' ) ) )
{
@@ -728,17 +863,7 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Request::i()->confirmedDelete( 'reg_cancel', 'reg_cancel_confirm', 'reg_cancel' );
/* Log the user out. Previously, we immediately deleted the account however this has been changed to let the cleanup task handle this instead. */
\IPS\Request::i()->setCookie( 'member_id', NULL );
\IPS\Request::i()->setCookie( 'pass_hash', NULL );
\IPS\Request::i()->setCookie( 'anon_login', NULL );
foreach( \IPS\Request::i()->cookie as $name => $value )
{
if( mb_strpos( $name, "ipbforumpass_" ) !== FALSE )
{
\IPS\Request::i()->setCookie( $name, NULL );
}
}
\IPS\Request::i()->clearLoginCookies();
/* Reset session global parameters */
$_SESSION = array();