Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

@@ -11,7 +11,7 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _register extends \IPS\Dispatcher\Controller
{
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = FALSE;
/**
* Constructor
*
@@ -50,6 +55,10 @@ class _register extends \IPS\Dispatcher\Controller
}
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
if ( isset( \IPS\Request::i()->oauth ) )
{
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimalNoHome';
}
\IPS\Output::i()->sidebar['enabled'] = FALSE;
}
@@ -69,21 +78,39 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=core&module=system&controller=register', NULL, 'register' ), array(), 'loc_registering' );
\IPS\Output::i()->allowDefaultWidgets = FALSE;
/* What's the "log in" link? */
$loginUrl = \IPS\Http\Url::internal( 'app=core&module=system&controller=login', NULL, 'login' );
if ( isset( \IPS\Request::i()->oauth ) and $ref = static::_refUrl() and $ref->base === 'none' )
{
$loginUrl = $ref;
}
/* Post before registering? */
$postBeforeRegister = NULL;
if ( isset( \IPS\Request::i()->cookie['post_before_register'] ) or isset( \IPS\Request::i()->pbr ) )
{
try
{
$postBeforeRegister = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( 'secret=?', \IPS\Request::i()->pbr ?: \IPS\Request::i()->cookie['post_before_register'] ) )->first();
}
catch ( \UnderflowException $e ) { }
}
/* Quick registration does not work with COPPA */
if ( \IPS\Login::registrationType() == 'normal' )
{
$form = $this->_registrationForm();
$form = $this->_registrationForm( $postBeforeRegister );
$form->class = 'ipsForm_fullWidth';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('registration');
if( \IPS\Request::i()->isAjax() )
{
\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' ), \IPS\Login::LOGIN_REGISTRATION_FORM ) );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupRegisterTemplate' ), new \IPS\Login( $loginUrl, \IPS\Login::LOGIN_REGISTRATION_FORM ), $postBeforeRegister );
}
else
{
\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' ), \IPS\Login::LOGIN_REGISTRATION_FORM ) ), new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=login', 'front', 'login' ) ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->register( $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupRegisterTemplate' ), new \IPS\Login( $loginUrl, \IPS\Login::LOGIN_REGISTRATION_FORM ), $postBeforeRegister ), new \IPS\Login( $loginUrl ), $postBeforeRegister );
}
return;
@@ -109,12 +136,12 @@ class _register extends \IPS\Dispatcher\Controller
/* If coppa is enabled we need to add a birthday verification */
if ( \IPS\Settings::i()->use_coppa OR \IPS\Settings::i()->minimum_age > 0 )
{
$steps['coppa'] = function( $data )
$steps['coppa'] = function( $data ) use ( $postBeforeRegister )
{
/* Build the form */
$form = new \IPS\Helpers\Form( 'coppa', 'register_button' );
$form->add( new \IPS\Helpers\Form\Date( 'bday', NULL, TRUE, array( 'max' => \IPS\DateTime::create() ) ) );
$form->add( new \IPS\Helpers\Form\Date( 'bday', NULL, TRUE, array( 'max' => \IPS\DateTime::create(), 'htmlAutocomplete' => "bday" ) ) );
if( $values = $form->values() )
{
/* Did we pass the minimum age requirement? */
@@ -135,60 +162,56 @@ class _register extends \IPS\Dispatcher\Controller
return $values;
}
return \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->coppa( $form );
return \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->coppa( $form, $postBeforeRegister );
};
}
$self = $this;
$steps['basic_info'] = function ( $data ) use ( $self )
$steps['basic_info'] = function ( $data ) use ( $self, $postBeforeRegister, $loginUrl )
{
$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' ), \IPS\Login::LOGIN_REGISTRATION_FORM ) );
$form = $this->_registrationForm( $postBeforeRegister );
return \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->register( $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupRegisterTemplate' ), new \IPS\Login( $loginUrl, \IPS\Login::LOGIN_REGISTRATION_FORM ), $postBeforeRegister ), new \IPS\Login( $loginUrl, \IPS\Login::LOGIN_REGISTRATION_FORM ), $postBeforeRegister );
};
/* 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 );
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard( $steps, \IPS\Http\Url::internal( 'app=core&module=system&controller=register' ), FALSE );
}
/**
* Normal registration form
*
* @param array|NULL $postBeforeRegister The row from core_post_before_registering if applicable
* @return \IPS\Form
*/
protected function _registrationForm()
protected function _registrationForm( $postBeforeRegister )
{
$form = \IPS\core\modules\front\system\register::buildRegistrationForm();
$form = static::buildRegistrationForm( $postBeforeRegister );
/* Handle submissions */
if ( $values = $form->values() )
{
$profileFields = array();
if( \IPS\Login::registrationType() == 'full' )
{
foreach ( \IPS\core\ProfileFields\Field::fields( array(), \IPS\core\ProfileFields\Field::REG ) as $group => $fields )
{
foreach ( $fields as $id => $field )
{
if( $field->required and ( $values[ $field->name ] === NULL or !isset( $values[ $field->name ] ) ) )
if ( $field instanceof \IPS\Helpers\Form\Upload )
{
\IPS\Output::i()->error( 'reg_required_fields', '1C223/5', 403, '' );
$profileFields[ "field_{$id}" ] = (string) $values[ $field->name ];
}
$profileFields[ "field_{$id}" ] = $field::stringValue( $values[ $field->name ] );
if ( $fields instanceof \IPS\Helpers\Form\Editor )
else
{
$field->claimAttachments( $this->id );
$profileFields[ "field_{$id}" ] = $field::stringValue( !empty( $values[ $field->name ] ) ? $values[ $field->name ] : NULL );
}
}
}
}
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'] ) ) )
{
@@ -213,40 +236,20 @@ class _register extends \IPS\Dispatcher\Controller
if ( !$form->error )
{
/* Create Member */
$member = \IPS\core\modules\front\system\register::_createMember( $values, $profileFields );
$member = static::_createMember( $values, $profileFields, $postBeforeRegister, $form );
/* Log them in */
\IPS\Session::i()->setMember( $member );
\IPS\Member\Device::loadOrCreate( $member, FALSE )->updateAfterAuthentication( TRUE, NULL );
if ( isset( \IPS\Request::i()->ref ) and $ref = @base64_decode( \IPS\Request::i()->ref ) )
{
try
{
$ref = \IPS\Http\Url::createFromString( $ref );
if ( !( $ref instanceof \IPS\Http\Url\Internal ) or $ref->base !== 'front' or $ref->openRedirect() )
{
throw new \Exception;
}
}
catch ( \Exception $e )
{
$ref = \IPS\Http\Url::internal('');
}
}
else
{
$ref = \IPS\Http\Url::internal('');
}
/* Redirect */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'redirect' => (string) $ref ) );
\IPS\Output::i()->json( array( 'redirect' => (string) $this->_performRedirect( $postBeforeRegister, TRUE ) ) );
}
else
{
\IPS\Output::i()->redirect( $ref );
$this->_performRedirect( $postBeforeRegister );
}
}
}
@@ -270,59 +273,75 @@ class _register extends \IPS\Dispatcher\Controller
$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( !isset( \IPS\Request::i()->finishStarted ) )
{
if ( $step->required AND !$step->completed( \IPS\Member::loggedIn() ) )
$haveRequired = FALSE;
foreach( $steps AS $id => $step )
{
$haveRequired = TRUE;
break;
if ( $step->required AND !$step->completed( \IPS\Member::loggedIn() ) )
{
$haveRequired = TRUE;
break;
}
}
if ( $haveRequired === FALSE )
{
/* Nope, forward */
$this->_performRedirect();
}
/* Make sure we reset any temp data that might have been stored in the session */
if( isset( $_SESSION['profileCompletionData'] ) )
{
unset( $_SESSION['profileCompletionData'] );
}
}
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' );
$url = \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=finish&finishStarted=1', 'front', 'register' )->setQueryString( 'ref', \IPS\Request::i()->ref );
foreach( \IPS\Application::allExtensions( 'core', 'ProfileSteps' ) AS $extension )
{
if ( method_exists( $extension, 'wizard') AND count( $extension::wizard() ) )
if ( method_exists( $extension, 'wizard') AND \is_array( $extension::wizard() ) AND \count( $extension::wizard() ) )
{
$wizardSteps = array_merge( $wizardSteps, $extension::wizard() );
}
if ( method_exists( $extension, 'extraStep') AND \count( $extension::extraStep() ) )
{
$wizardSteps = array_merge( $wizardSteps, $extension::extraStep() );
}
}
$wizardSteps = \IPS\Member\ProfileStep::setOrder( $wizardSteps );
$wizardSteps = array_merge( $wizardSteps, array( 'profile_done' => function( $data ) {
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ), 'saved' );
$this->_performRedirect( NULL, FALSE, 'saved' );
} ) );
$wizard = new \IPS\Helpers\Wizard( $wizardSteps, $url, TRUE, NULL, TRUE );
$wizard->template = array( \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' ), 'completeWizardTemplate' );
\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 );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->finishRegistration( (string) $wizard );
}
/**
* Build Registration Form
*
* @param array|NULL $postBeforeRegister The row from core_post_before_registering if applicable
* @return \IPS\Helpers\Form
*/
public static function buildRegistrationForm()
{
public static function buildRegistrationForm( $postBeforeRegister = NULL )
{
/* Build the form */
$form = new \IPS\Helpers\Form( 'form', 'register_button', NULL, array( 'data-controller' => 'core.front.system.register') );
$form->add( new \IPS\Helpers\Form\Text( 'username', NULL, TRUE, array( 'accountUsername' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'accountEmail' => TRUE, 'maxLength' => 150, 'bypassProfanity' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password', NULL, TRUE, array( 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'checkStrength' => TRUE, 'bypassProfanity' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', NULL, TRUE, array( 'confirm' => 'password', 'bypassProfanity' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'username', NULL, TRUE, array( 'accountUsername' => TRUE, 'htmlAutocomplete' => "username" ) ) );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', $postBeforeRegister ? $postBeforeRegister['email'] : NULL, TRUE, array( 'accountEmail' => TRUE, 'maxLength' => 150, 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL, 'htmlAutocomplete' => "email" ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password', NULL, TRUE, array( 'protect' => TRUE, 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'checkStrength' => TRUE, 'strengthRequest' => array( 'username', 'email_address' ), 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL, 'htmlAutocomplete' => "new-password" ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', NULL, TRUE, array( 'protect' => TRUE, 'confirm' => 'password', 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL, 'htmlAutocomplete' => "new-password" ) ) );
/* Profile fields */
if ( \IPS\Login::registrationType() == 'full' )
@@ -358,7 +377,7 @@ class _register extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Text( 'q_and_a', NULL, TRUE, array(), function( $val )
{
$qanda = intval( \IPS\Request::i()->q_and_a_id );
$qanda = \intval( \IPS\Request::i()->q_and_a_id );
$pass = true;
if( $qanda )
@@ -376,7 +395,7 @@ class _register extends \IPS\Dispatcher\Controller
if( $answers )
{
$answers = is_array( $answers ) ? $answers : array( $answers );
$answers = \is_array( $answers ) ? $answers : array( $answers );
$pass = FALSE;
foreach( $answers as $answer )
@@ -421,7 +440,7 @@ class _register extends \IPS\Dispatcher\Controller
$form->addSeparator();
}
$form->add( new \IPS\Helpers\Form\Checkbox( 'reg_admin_mails', \IPS\Settings::i()->updates_consent_default == 'enabled' ? TRUE : FALSE, FALSE ) );
$form->add( new \IPS\Helpers\Form\Checkbox( 'reg_admin_mails', ( \IPS\Settings::i()->updates_consent_default == 'enabled' or \IPS\Request::i()->newsletter ) ? TRUE : FALSE, FALSE ) );
\IPS\core\modules\front\system\register::buildRegistrationTerm();
@@ -447,7 +466,7 @@ class _register extends \IPS\Dispatcher\Controller
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' ) ) )
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();
@@ -461,7 +480,7 @@ class _register extends \IPS\Dispatcher\Controller
if ( \IPS\Settings::i()->security_questions_prompt === 'optional' )
{
$securityOptoutToggles = array();
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
foreach ( range( 1, min( $numberOfQuestions, \count( $securityQuestions ) ) ) as $i )
{
$securityOptoutToggles[] = 'security_question_q_' . $i;
$securityOptoutToggles[] = 'security_question_a_' . $i;
@@ -474,7 +493,7 @@ class _register extends \IPS\Dispatcher\Controller
}
$form->add( $optOutCheckbox );
}
foreach ( range( 1, min( $numberOfQuestions, count( $securityQuestions ) ) ) as $i )
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 ) ) )
@@ -499,11 +518,13 @@ class _register extends \IPS\Dispatcher\Controller
/**
* Create Member
*
* @param array $values Values from form
* @param array $profileFields Profile field values from registration
* @param array $values Values from form
* @param array $profileFields Profile field values from registration
* @param array|NULL $postBeforeRegister The row from core_post_before_registering if applicable
* @param \IPS\Helpers\Form $form The form object
* @return \IPS\Member
*/
public static function _createMember( $values, $profileFields )
public static function _createMember( $values, $profileFields, $postBeforeRegister = NULL, &$form )
{
/* Create */
$member = new \IPS\Member;
@@ -515,6 +536,15 @@ class _register extends \IPS\Dispatcher\Controller
$member->members_bitoptions['view_sigs'] = TRUE;
$member->last_visit = time();
if( isset( \IPS\Request::i()->cookie['language'] ) AND \IPS\Request::i()->cookie['language'] )
{
$member->language = \IPS\Request::i()->cookie['language'];
}
elseif ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) )
{
$member->language = \IPS\Lang::autoDetectLanguage( $_SERVER['HTTP_ACCEPT_LANGUAGE'] );
}
/* Query spam service */
$spamCode = NULL;
$spamAction = NULL;
@@ -545,7 +575,7 @@ class _register extends \IPS\Dispatcher\Controller
$member->logHistory( 'core', 'account', array( 'type' => 'register', 'spamCode' => $spamCode, 'spamAction' => $spamAction ), FALSE );
/* Security Questions */
if ( \IPS\Settings::i()->security_questions_enabled and in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
if ( \IPS\Settings::i()->security_questions_enabled and \in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
{
if ( isset( $values['security_questions_optout_title'] ) )
{
@@ -570,7 +600,7 @@ class _register extends \IPS\Dispatcher\Controller
}
}
if ( count( $answers ) )
if ( \count( $answers ) )
{
\IPS\Db::i()->insert( 'core_security_answers', $answers );
}
@@ -583,6 +613,16 @@ class _register extends \IPS\Dispatcher\Controller
$member->save();
}
/* Cycle profile fields */
foreach( $profileFields as $id => $fieldValue )
{
$field = \IPS\core\ProfileFields\Field::loadWithMember( mb_substr( $id, 6 ), NULL, NULL, NULL );
if( $field->type == 'Editor' )
{
$field->claimAttachments( $member->member_id );
}
}
/* Save custom field values */
\IPS\Db::i()->replace( 'core_pfields_content', array_merge( array( 'member_id' => $member->member_id ), $profileFields ) );
@@ -597,8 +637,11 @@ class _register extends \IPS\Dispatcher\Controller
$member->logHistory( 'core', 'terms_acceptance', array( 'type' => 'terms' ) );
/* Handle validation */
$member->postRegistration();
/* Handle validation, but not if we were flagged as a spammer and banned */
if( $spamAction != 3 )
{
$member->postRegistration( FALSE, FALSE, $postBeforeRegister, static::_refUrl() );
}
/* Save and return */
return $member;
@@ -643,7 +686,6 @@ class _register extends \IPS\Dispatcher\Controller
{
/* They are cancelled and will be deleted, haha, etc */
\IPS\Output::i()->error( 'reg_is_cancelled', '2C223/9', 403, '' );
}
else if ( $validating['user_verified'] )
{
@@ -669,7 +711,7 @@ class _register extends \IPS\Dispatcher\Controller
$validating = \IPS\Db::i()->select( '*', 'core_validating', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ) );
if ( !count( $validating ) )
if ( !\count( $validating ) )
{
\IPS\Output::i()->error( 'validate_no_record', '2S129/3', 404, '' );
}
@@ -696,6 +738,9 @@ class _register extends \IPS\Dispatcher\Controller
*/
protected function validate()
{
/* Prevent the vid key from being exposed in referrers */
\IPS\Output::i()->sendHeader( "Referrer-Policy: origin" );
if( \IPS\Request::i()->vid AND \IPS\Request::i()->mid )
{
/* Load record */
@@ -705,31 +750,71 @@ class _register extends \IPS\Dispatcher\Controller
}
catch ( \UnderflowException $e )
{
\IPS\Output::i()->error( 'validate_no_record', '2S129/2', 404, '' );
$this->_performRedirect( NULL, FALSE, 'validate_no_record' );
}
/* Validate */
$member = \IPS\Member::load( \IPS\Request::i()->mid );
if ( $record['new_reg'] )
{
$member->emailValidationConfirmed( $record );
}
else
{
$member->members_bitoptions['validating'] = FALSE;
$member->save();
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=?', $member->member_id ) );
/* Send a confirmation email */
\IPS\Email::buildFromTemplate( 'core', 'email_address_changed', array( $member, $record['prev_email'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $record['prev_email'], array(), array(), NULL, NULL, array( 'Reply-To' => \IPS\Settings::i()->email_in ) );
if ( isset( $record['ref'] ) )
{
\IPS\Request::i()->ref = base64_encode( $record['ref'] );
}
/* If this is a new registration and the user has already validated their email, redirect */
if ( $record['new_reg'] AND $record['user_verified'] )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=validating', 'front', 'register' ), 'reg_email_already_validated_admin' );
}
$member = \IPS\Member::load( \IPS\Request::i()->mid );
/* 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' );
/* Post before registering? */
try
{
$postBeforeRegister = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( '`member`=?', $member->member_id ) )->first();
}
catch ( \UnderflowException $e )
{
$postBeforeRegister = NULL;
}
/* Ask the user to confirm - this prevents spiders and similar scrapers seeing the link and following it without the user's knowledge */
$form = new \IPS\Helpers\Form( 'form', 'validate_my_account' );
$form->hiddenValues['custom'] = 'submitted';
if( $submitted = $form->values() )
{
/* Validate */
if ( $record['new_reg'] )
{
$member->emailValidationConfirmed( $record );
}
else
{
$member->members_bitoptions['validating'] = FALSE;
$member->save();
$member->memberSync( 'onEmailChange', array( $member->email, $record['prev_email'] ) );
\IPS\Db::i()->delete( 'core_validating', array( 'member_id=?', $member->member_id ) );
/* Send a confirmation email */
\IPS\Email::buildFromTemplate( 'core', 'email_address_changed', array( $member, $record['prev_email'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $record['prev_email'], array(), array(), NULL, NULL, array( 'Reply-To' => \IPS\Settings::i()->email_in ) );
}
/* Log in */
\IPS\Session::i()->setMember( $member );
\IPS\Member\Device::loadOrCreate( $member )->updateAfterAuthentication( TRUE );
/* Redirect */
$this->_performRedirect( $postBeforeRegister, FALSE, 'validate_email_confirmation' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_complete_details');
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' ), 'completeValidation' ), $member );
return;
}
/* If we're still here, just redirect to homepage */
$this->_performRedirect( NULL, FALSE, 'validate_no_record' );
}
/**
@@ -827,32 +912,22 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Member::loggedIn()->logHistory( 'core', 'account', array( 'type' => 'complete' ), FALSE );
/* Handle validation */
\IPS\Member::loggedIn()->postRegistration( ( isset( $values['email_address'] ) ) ? FALSE : TRUE );
$postBeforeRegister = NULL;
if ( isset( \IPS\Request::i()->cookie['post_before_register'] ) )
{
try
{
$postBeforeRegister = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( 'secret=?', \IPS\Request::i()->cookie['post_before_register'] ) )->first();
}
catch ( \UnderflowException $e ) { }
}
\IPS\Member::loggedIn()->postRegistration( ( isset( $values['email_address'] ) ) ? FALSE : TRUE, FALSE, $postBeforeRegister, static::_refUrl() );
/* Set member as a full member in the session table */
\IPS\Session::i()->setType( \IPS\Session\Front::LOGIN_TYPE_MEMBER );
/* Redirect */
if ( isset( \IPS\Request::i()->ref ) and $ref = @base64_decode( \IPS\Request::i()->ref ) )
{
try
{
$ref = \IPS\Http\Url::createFromString( $ref );
if ( !( $ref instanceof \IPS\Http\Url\Internal ) or $ref->base !== 'front' or $ref->openRedirect() )
{
throw new \Exception;
}
}
catch ( \Exception $e )
{
$ref = \IPS\Http\Url::internal('');
}
}
else
{
$ref = \IPS\Http\Url::internal('');
}
\IPS\Output::i()->redirect( $ref );
$this->_performRedirect( $postBeforeRegister );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_complete_details');
@@ -881,11 +956,19 @@ class _register extends \IPS\Dispatcher\Controller
{
$pending = null;
}
/* If we're pending *admin* validation, don't let them change their email - otherwise doing so would allow them to bypass validation
@todo - this is kind of an unsatisfcatory solution as ideally it would put them back into admin approval, but this would require
significant reengineering to address - see commit notes on this code block */
if ( $pending and $pending['new_reg'] and $pending['user_verified'] )
{
\IPS\Output::i()->error( 'no_module_permission', '2C223/6', 403, '' );
}
/* Build the form */
$form = new \IPS\Helpers\Form;
$form->class = 'ipsForm_vertical';
$form->add( new \IPS\Helpers\Form\Email( 'new_email', '', TRUE, array( 'accountEmail' => \IPS\Member::loggedIn() ) ) );
$form->add( new \IPS\Helpers\Form\Email( 'new_email', '', TRUE, array( 'accountEmail' => \IPS\Member::loggedIn(), 'htmlAutocomplete' => "email" ) ) );
$captcha = new \IPS\Helpers\Form\Captcha;
if ( (string) $captcha !== '' )
{
@@ -931,7 +1014,7 @@ class _register extends \IPS\Dispatcher\Controller
}
/* If email validation is required, do that... */
if ( in_array( \IPS\Settings::i()->reg_auth_type, array( 'user', 'admin_user' ) ) )
if ( \in_array( \IPS\Settings::i()->reg_auth_type, array( 'user', 'admin_user' ) ) )
{
/* Delete any pending validation emails */
if ( $pending['vid'] )
@@ -959,7 +1042,7 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
}
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
}
/**
@@ -980,22 +1063,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()->clearLoginCookies();
/* Reset session global parameters */
$_SESSION = array();
/* We have to explicitly reset the session cookie
@link http://php.net/manual/en/function.session-destroy.php */
$params = session_get_cookie_params();
setcookie( session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] );
/* Then destroy the session itself */
session_destroy();
/* Member sync callback */
\IPS\Member::loggedIn()->memberSync( 'onLogout', array( \IPS\Http\Url::internal( '' ) ) );
\IPS\Login::logout();
/* Flag user as having cancelled their registration */
\IPS\Db::i()->update( 'core_validating', array( 'reg_cancelled' => time() ), array( 'member_id=? AND new_reg=1', \IPS\Member::loggedIn()->member_id ) );
@@ -1043,25 +1111,55 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Member::loggedIn()->members_bitoptions['must_reaccept_terms'] = FALSE;
\IPS\Member::loggedIn()->save();
if ( isset( \IPS\Request::i()->ref ) )
$this->_performRedirect();
}
$subprocessors = array();
/* Work out the main subprocessors that the user has no direct choice over */
if ( \IPS\Settings::i()->privacy_show_processors )
{
foreach( \IPS\Application::enabledApplications() as $app )
{
try
{
$ref = \IPS\Http\Url\Friendly::createFromString( base64_decode( \IPS\Request::i()->ref ) );
if ( $ref instanceof \IPS\Http\Url\Internal and !$ref->openRedirect() )
{
\IPS\Output::i()->redirect( $ref );
}
}
catch ( \Exception $e ) { }
$subprocessors = array_merge( $subprocessors, $app->privacyPolicyThirdParties() );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('terms_of_use');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('system')->reconfirmTerms( \IPS\Member::loggedIn()->members_bitoptions['must_reaccept_terms'], \IPS\Member::loggedIn()->members_bitoptions['must_reaccept_privacy'], $form );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('system')->reconfirmTerms( \IPS\Member::loggedIn()->members_bitoptions['must_reaccept_terms'], \IPS\Member::loggedIn()->members_bitoptions['must_reaccept_privacy'], $form, $subprocessors );
}
/**
* Cancel the post before registering submission
*
* @return void
*/
protected function cancelPostBeforeRegister()
{
if( ! isset( \IPS\Request::i()->id ) or ! isset( \IPS\Request::i()->pbr ) or ! \IPS\Settings::i()->post_before_registering )
{
\IPS\Output::i()->error( 'no_module_permission', '2C223/D', 403, '' );
}
try
{
$pbr = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( "id=? and secret=?", \IPS\Request::i()->id, \IPS\Request::i()->pbr ) )->first();
$class = $pbr['class'];
try
{
$class::load( $pbr['id'] )->delete();
}
catch ( \OutOfRangeException $e ) { }
\IPS\Db::i()->delete( 'core_post_before_registering', array( 'class=? AND id=?', $pbr['class'], $pbr['id'] ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal(''), 'post_before_register_submission_cancelled' );
}
catch( \UnderFlowException $e )
{
\IPS\Output::i()->error( 'pbr_row_not_found', '2C223/E', 403, '' );
}
}
/**
@@ -1083,4 +1181,129 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Member::loggedIn()->language()->words[ "reg_agreed_terms" ] .= sprintf( \IPS\Member::loggedIn()->language()->get("reg_privacy_link"), \IPS\Http\Url::external( \IPS\Settings::i()->privacy_link ), 'target="_blank" rel="noopener"' );
}
}
/**
* Redirect the user
* -consolidated to reduce duplicate code
*
* @param array|NULL $postBeforeRegister Post before registration data
* @param bool $return Return the URL instead of redirecting
* @param string $message (Optional) message to show during redirect
* @return void
*/
protected function _performRedirect( $postBeforeRegister=NULL, $return=FALSE, $message='' )
{
/* Redirect */
if ( $ref = static::_refUrl() )
{
// We got it!
}
elseif ( $postBeforeRegister )
{
try
{
$class = $postBeforeRegister['class'];
$ref = $class::load( $postBeforeRegister['id'] )->url();
}
catch ( \OutOfRangeException $e )
{
$ref = \IPS\Http\Url::internal('');
}
}
else
{
$ref = \IPS\Http\Url::internal('');
}
if( $return === TRUE )
{
return $ref;
}
\IPS\Output::i()->redirect( $ref, $message );
}
/**
* Get referral URL
*
* @return \IPS\Http\Url|NULL
*/
protected static function _refUrl()
{
if ( isset( \IPS\Request::i()->ref ) and $ref = @base64_decode( \IPS\Request::i()->ref ) )
{
try
{
$ref = \IPS\Http\Url::createFromString( $ref );
if ( ( $ref instanceof \IPS\Http\Url\Internal ) and \in_array( $ref->base, array( 'front', 'none' ) ) and !$ref->openRedirect() )
{
return $ref;
}
}
catch ( \Exception $e ){ }
}
return NULL;
}
/**
* Set Password
*
* @return void
*/
public function setPassword(): void
{
try
{
$member = \IPS\Member::load( \IPS\Request::i()->mid );
if ( !$member->member_id )
{
throw new \OutOfRangeException;
}
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C223/F', 403, '' );
}
/* If this user isn't being forced, error */
if ( !$member->members_bitoptions['password_reset_forced'] )
{
\IPS\Output::i()->error( 'node_error', '2C223/H', 403, '' );
}
if ( !\IPS\Login::compareHashes( md5( \IPS\SUITE_UNIQUE_KEY . $member->email . $member->name ), (string) \IPS\Request::i()->passkey ) )
{
\IPS\Output::i()->error( 'node_error', '2C223/G', 403, '' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'set_password_title', NULL, array( 'sprintf' => array( $member->name ) ) );
if ( $mfa = \IPS\MFA\MFAHandler::accessToArea( 'core', 'AuthenticateFront', \IPS\Request::i()->url(), $member ) )
{
\IPS\Output::i()->output = $mfa;
return;
}
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Password( 'password', NULL, TRUE, array( 'protect' => TRUE, 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'checkStrength' => TRUE, 'strengthRequest' => array( 'username', 'email_address' ), 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL, 'htmlAutocomplete' => "new-password" ) ) );
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', NULL, TRUE, array( 'protect' => TRUE, 'confirm' => 'password', 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL, 'htmlAutocomplete' => "new-password" ) ) );
if ( $values = $form->values() )
{
$changed = $member->changePassword( $values['password'], 'forced' );
if ( !$changed and \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' ) )
{
$member->setLocalPassword( $values['password'] );
$member->save();
}
\IPS\Request::i()->setCookie( 'noCache', 1 );
$success = new \IPS\Login\Success( $member, \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' ) );
$success->process();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ), 'set_password_stored' );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->registerSetPassword( $form, $member );
}
}