Version 4.7.13

This commit is contained in:
Neo committed 2025-12-19 16:14:56 -08:00
1 parent fd22d99e69
commit 7124a02564
954 files changed
+29277 -59308

No files matched your search

@@ -314,7 +314,7 @@ class _ajax extends \IPS\Dispatcher\Controller
public function topContributors()
{
/* How many? */
$limit = \intval( ( isset( \IPS\Request::i()->limit ) and \IPS\Request::i()->limit <= 25 ) ? \IPS\Request::i()->limit : 5 );
$limit = \intval( ( isset( \IPS\Request::i()->limit ) and \IPS\Request::i()->limit <= 25 and \IPS\Request::i()->limit > 0 ) ? \IPS\Request::i()->limit : 5 );
/* What timeframe? */
$where = array( array( 'member_received > 0' ) );
@@ -11,6 +11,10 @@
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Helpers\Form\YesNo;
use IPS\Output;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -18,12 +22,19 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
}
/**
* Cookie Policy
* Cookie Information Page
*/
class _cookies extends \IPS\Dispatcher\Controller
{
public function execute()
{
parent::execute();
\IPS\Output::i()->pageCaching = FALSE;
}
/**
* Privacy Policy
* Cookie Information Page
*
* @return void
*/
@@ -35,7 +46,35 @@ class _cookies extends \IPS\Dispatcher\Controller
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('cookies_about') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cookies_about');
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->cookies();
}
/**
* Opt out of optional cookies
*
* @return void
*/
protected function cookieConsentToggle()
{
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->setAllowOptionalCookies( (bool) \IPS\Request::i()->status );
if ( isset( \IPS\Request::i()->ref ) )
{
try
{
$url = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
}
catch( \IPS\Http\Url\Exception $e )
{
$url = NULL;
}
if ( $url instanceof \IPS\Http\Url\Internal and !$url->openRedirect() )
{
\IPS\Output::i()->redirect( $url );
}
}
}
}
@@ -394,7 +394,7 @@ class _register extends \IPS\Dispatcher\Controller
throw new \DomainException( 'q_and_a_incorrect' );
}
$answers = json_decode( $question['qa_answers'] );
$answers = json_decode( $question['qa_answers'], true );
if( $answers )
{
@@ -551,9 +551,11 @@ class _register extends \IPS\Dispatcher\Controller
/* Query spam service */
$spamCode = NULL;
$spamAction = NULL;
$disposable = FALSE;
$geoBlock = FALSE;
if( \IPS\Settings::i()->spam_service_enabled )
{
$spamAction = $member->spamService( 'register', NULL, $spamCode );
$spamAction = $member->spamService( 'register', NULL, $spamCode, $disposable, $geoBlock );
if( $spamAction == 4 )
{
\IPS\Output::i()->error( 'spam_denied_account', '2S129/1', 403, '' );
@@ -575,7 +577,7 @@ class _register extends \IPS\Dispatcher\Controller
/* Save anything the profile extensions did */
$member->save();
$member->logHistory( 'core', 'account', array( 'type' => 'register', 'spamCode' => $spamCode, 'spamAction' => $spamAction ), FALSE );
$member->logHistory( 'core', 'account', array( 'type' => 'register', 'spamCode' => $spamCode, 'spamAction' => $spamAction, 'disposable' => $disposable, 'geoBlock' => $geoBlock ), FALSE );
/* Security Questions */
if ( \IPS\Settings::i()->security_questions_enabled and \in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
@@ -834,6 +836,11 @@ class _register extends \IPS\Dispatcher\Controller
}
elseif ( \IPS\Member::loggedIn()->real_name and \IPS\Member::loggedIn()->email )
{
/* If we somehow came here from the oauth authorization prompt but the member, redirect back there */
if ( isset( \IPS\Request::i()->oauth ) and $ref = static::_refUrl() and $ref->base === 'none' )
{
\IPS\Output::i()->redirect( $ref );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
}
@@ -138,7 +138,6 @@ class _settings extends \IPS\Dispatcher\Controller
/* Show our own oauth clients? */
$showApps = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( array( 'oauth_enabled=1 AND oauth_ucp=1' ) ) )->first();
/* Return */
return \IPS\Theme::i()->getTemplate( 'system' )->settings( $area, $output, ( \IPS\Settings::i()->allow_email_changes != 'disabled' ), $canChangePassword, \IPS\Member::loggedIn()->group['g_dname_changes'], $canChangeSignature, $loginMethods, $canConfigureMfa, $showApps );
}
@@ -240,8 +239,14 @@ class _settings extends \IPS\Dispatcher\Controller
/* Build the form */
$form = new \IPS\Helpers\Form;
$form->class = 'ipsForm_collapseTablet';
$form->addDummy( 'current_email', htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE ) );
$form->add( new \IPS\Helpers\Form\Email( 'new_email', '', TRUE, array( 'accountEmail' => \IPS\Member::loggedIn() ) ) );
$currentEmail = htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE );
$form->addDummy( 'current_email', \IPS\Member::loggedIn()->members_bitoptions["email_messages_bounce"] ? \IPS\Theme::i()->getTemplate( 'global', 'core' )->memberEmailBlockedMessage( $currentEmail ) : $currentEmail );
$form->add( new \IPS\Helpers\Form\Email(
'new_email',
'',
TRUE,
array( 'accountEmail' => \IPS\Member::loggedIn() )
) );
/* Handle submissions */
$values = NULL;
@@ -634,7 +639,7 @@ class _settings extends \IPS\Dispatcher\Controller
return \IPS\Theme::i()->getTemplate( 'system' )->settingsMfaPassword( $login, $error );
}
}
/* Get our handlers and the output, even if it's just for a backdrop */
$handlers = array();
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
@@ -683,6 +688,9 @@ class _settings extends \IPS\Dispatcher\Controller
\IPS\Member::loggedIn()->members_bitoptions['security_questions_opt_out'] = FALSE;
\IPS\Member::loggedIn()->save();
/* Invalidate other sessions */
\IPS\Member::loggedIn()->invalidateSessionsAndLogins( \IPS\Session::i()->id );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
}
@@ -720,10 +728,128 @@ class _settings extends \IPS\Dispatcher\Controller
return;
}
}
$output.= \IPS\Theme::i()->getTemplate( 'system' )->settingsPrivacy();
/* If we're still here, just show the screen */
return $output;
}
/**
* Request personal identifiable information
*
* @return void
*/
protected function requestPiiData()
{
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member\PrivacyAction::canRequestPiiData() OR \IPS\Settings::i()->pii_type !== 'on' )
{
\IPS\Output::i()->error( 'node_error', '1C122/10', 403, '' );
}
\IPS\Member\PrivacyAction::requestPiiData();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')->setFragment('piiDataRequest'), 'pii_requested' );
}
/**
* Download personal identifiable information
*
* @return void
* @throws \Exception
*/
protected function downloadPiiData()
{
\IPS\Session::i()->csrfCheck();
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member\PrivacyAction::canDownloadPiiData() OR \IPS\Settings::i()->pii_type !== 'on' )
{
\IPS\Output::i()->error( 'node_error', '1C122/11', 403, '' );
}
$xml = \IPS\Member::loggedIn()->getPiiData();
\IPS\Db::i()->delete( 'core_member_privacy_actions', array( 'member_id=? AND action=?', \IPS\Member::loggedIn()->member_id, \IPS\Member\PrivacyAction::TYPE_REQUEST_PII ) );
\IPS\Db::i()->delete( 'core_notifications', array( 'member=? AND notification_key=?', \IPS\Member::loggedIn()->member_id, 'pii_data' ) );
\IPS\Member::loggedIn()->logHistory( 'core', 'privacy', array( 'type' => 'pii_download' ) );
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', [ 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', \IPS\Member::loggedIn()->name.'_personal_information.xml' ) ], FALSE, FALSE, FALSE );
}
/**
* Request account deletion
*
* @return void
*/
protected function requestAccountDeletion()
{
\IPS\Session::i()->csrfCheck();
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member::loggedIn()->canUseAccountDeletion() OR \IPS\Settings::i()->right_to_be_forgotten_type !== 'on' )
{
\IPS\Output::i()->error( 'node_error', '2C122/13', 403, '' );
}
if( !\IPS\Member\PrivacyAction::canDeleteAccount() )
{
\IPS\Output::i()->error( 'node_error', '1C122/12', 403, '' );
}
if( \IPS\Request::i()->vkey )
{
\IPS\Member\PrivacyAction::requestAccountDeletion( NULL, FALSE );
}
else
{
\IPS\Member\PrivacyAction::requestAccountDeletion(NULL, TRUE );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')->setFragment('requestAccountDeletion'), 'account_deletion_requested' );
}
/**
* Cancel account deletion
*
* @return void
*/
protected function cancelAccountDeletion()
{
\IPS\Session::i()->csrfCheck();
try
{
$where = [];
$where[] = ['member_id=?', \IPS\Member::loggedIn()->member_id];
$where[] = [ \IPS\Db::i()->in( 'action',[\IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE, \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE_VALIDATION ] ) ];
$row = \IPS\Db::i()->select( '*', \IPS\Member\PrivacyAction::$databaseTable, $where )->first();
\IPS\Member\PrivacyAction::constructFromData( $row )->delete();
\IPS\Member::loggedIn()->logHistory( 'core', 'privacy', [ 'type' => 'account_deletion_cancelled' ] );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front'), 'account_deletion_cancelled' );
}
catch( \UnderflowException $e )
{
\IPS\Output::i()->error( 'node_error', '2C122/Y', 404, '' );
}
}
/**
* Confirm account deletion
*
* @return void
*/
protected function confirmAccountDeletion()
{
$key = \IPS\Request::i()->vid;
try
{
$request = \IPS\Member\PrivacyAction::getDeletionRequestByMemberAndKey( \IPS\Member::loggedIn(), $key );
$request->confirmAccountDeletion();
\IPS\Output::i()->redirect( $this->url->setQueryString( 'area','mfa'), 'account_deletion_confirmed' );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2C122/Z', 404, '' );
}
}
/**
* Initial MFA Setup
@@ -1225,8 +1351,9 @@ class _settings extends \IPS\Dispatcher\Controller
$content = nl2br( trim( $pre->nodeValue ) );
$preBreaks += \count( explode( "<br />", $content ) );
}
if ( \is_numeric( $sigLimits[5] ) and ( $signature->getElementsByTagName('p')->length + $signature->getElementsByTagName('br')->length + $preBreaks ) > $sigLimits[5] )
/* Line limit with a sensible length restriction to prevent broken html */
if ( ( \is_numeric( $sigLimits[5] ) and ( $signature->getElementsByTagName('p')->length + $signature->getElementsByTagName('br')->length + $preBreaks ) > $sigLimits[5] ) or \strlen( $values['signature'] ) > 20000 )
{
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_lines_exceeded');
}
@@ -1729,6 +1856,12 @@ class _settings extends \IPS\Dispatcher\Controller
{
\IPS\Session::i()->csrfCheck();
/* Check validation */
if( !isset( $_SESSION['passwordValidatedForMfa'] ) )
{
\IPS\Output::i()->error( 'node_error', '1C122/14', 403, '' );
}
/* Check this value can be toggled */
if( \IPS\Member::loggedIn()->group['g_hide_online_list'] >= 1 )
{