Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

+51 -79
View File
@@ -35,29 +35,39 @@ class _ajax extends \IPS\Dispatcher\Controller
$where = array( "name LIKE CONCAT(?, '%')" );
$binds = array( $input );
if ( \IPS\Dispatcher::i()->controllerLocation === 'admin' )
if ( \IPS\Dispatcher::i()->controllerLocation === 'admin' OR ( \IPS\Member::loggedIn()->modPermission('can_see_emails') AND \IPS\Request::i()->type == 'mod' ) )
{
$where[] = "email LIKE CONCAT(?, '%')";
$binds[] = $input;
}
if ( \IPS\Dispatcher::i()->controllerLocation === 'admin' )
{
if ( \is_numeric( \IPS\Request::i()->input ) )
{
$where[] = "member_id=?";
$binds[] = \intval( \IPS\Request::i()->input );
}
}
/* Build the array item for this member after constructing a record */
/* The value should be just the name so that it's inserted into the input properly, but for display, we wrap it in the group *fix */
foreach ( \IPS\Db::i()->select( '*', 'core_members', array_merge( array( implode( ' OR ', $where ) ), $binds ), 'LENGTH(name) ASC', array( 0, 20 ) ) as $row )
{
$member = \IPS\Member::constructFromData( $row );
$extra = \IPS\Dispatcher::i()->controllerLocation == 'admin' ? htmlspecialchars( $member->email, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : $member->groupName;
if ( \IPS\Member::loggedIn()->modPermission('can_see_emails') AND \IPS\Request::i()->type == 'mod' )
{
$extra = htmlspecialchars( $member->email, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) . '<br>' . $member->groupName;
}
$results[] = array(
'id' => $member->member_id,
'value' => $member->name,
'name' => \IPS\Dispatcher::i()->controllerLocation == 'admin' ? $member->group['prefix'] . htmlspecialchars( $member->name, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) . $member->group['suffix'] : htmlspecialchars( $member->name, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ),
'extra' => \IPS\Dispatcher::i()->controllerLocation == 'admin' ? htmlspecialchars( $member->email, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : $member->groupName,
'extra' => $extra,
'photo' => (string) $member->photo,
);
}
@@ -256,77 +266,6 @@ class _ajax extends \IPS\Dispatcher\Controller
\IPS\Output::i()->json( $result );
}
/**
* Returns boolean in json indicating whether the supplied email already exists
*
* @return void
*/
public function emailExists()
{
$result = array( 'result' => 'ok' );
/* The value comes urlencoded so we need to decode so length is correct (and not using a percent-encoded value) */
$email = urldecode( \IPS\Request::i()->input );
/* Check is valid */
if ( !$email )
{
$result = array( 'result' => 'fail', 'message' => \IPS\Member::loggedIn()->language()->addToStack('form_required') );
}
elseif ( filter_var( $email, FILTER_VALIDATE_EMAIL ) === FALSE )
{
$result = array( 'result' => 'fail', 'message' => \IPS\Member::loggedIn()->language()->addToStack('form_bad_value') );
}
/* Check if it exists */
else if ( $error = \IPS\Login::emailIsInUse( $email ) )
{
if ( \IPS\Member::loggedIn()->isAdmin() )
{
$result = array( 'result' => 'fail', 'message' => $error );
}
else
{
$result = array( 'result' => 'fail', 'message' => \IPS\Member::loggedIn()->language()->addToStack('member_email_exists') );
}
}
/* Check it's not banned */
if ( $result == array( 'result' => 'ok' ) )
{
foreach( \IPS\Db::i()->select( 'ban_content', 'core_banfilters', array("ban_type=?", 'email') ) as $bannedEmail )
{
if( preg_match( '/^' . str_replace( '\*', '.*', preg_quote( $bannedEmail, '/' ) ) . '$/i', $email ) )
{
$result = array( 'result' => 'fail', 'message' => \IPS\Member::loggedIn()->language()->addToStack('form_email_banned') );
break;
}
}
}
/* Check it's an allowed domain */
if ( \IPS\Settings::i()->allowed_reg_email !== '' AND $allowedEmailDomains = explode( ',', \IPS\Settings::i()->allowed_reg_email ) )
{
$matched = FALSE;
foreach ( $allowedEmailDomains AS $domain )
{
if( \mb_stripos( $email, "@" . $domain ) !== FALSE )
{
$matched = TRUE;
break;
}
}
if ( \count( $allowedEmailDomains ) AND !$matched )
{
$result = array( 'result' => 'fail', 'message' => $email );
}
}
\IPS\Output::i()->json( $result );
}
/**
* Get state/region list for country
*
@@ -396,6 +335,7 @@ class _ajax extends \IPS\Dispatcher\Controller
}
else
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'block_topContributors' );
\IPS\Output::i()->output = $output;
}
@@ -410,6 +350,11 @@ class _ajax extends \IPS\Dispatcher\Controller
{
/* How many? */
$limit = \intval( ( isset( \IPS\Request::i()->limit ) and \IPS\Request::i()->limit <= 25 ) ? \IPS\Request::i()->limit : 5 );
if( $limit < 0 )
{
$limit = 5;
}
/* What timeframe? */
$where = array( array( 'member_id > 0' ) );
@@ -454,6 +399,7 @@ class _ajax extends \IPS\Dispatcher\Controller
}
else
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'block_mostSolved' );
\IPS\Output::i()->output = $output;
}
@@ -472,6 +418,7 @@ class _ajax extends \IPS\Dispatcher\Controller
}
$preview = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->navBar( TRUE );
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/menumanager.css', 'core', 'admin' ) );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'applications', 'core', 'admin' )->menuPreviewWrapper( $preview ) );
}
@@ -608,7 +555,8 @@ class _ajax extends \IPS\Dispatcher\Controller
{
$mysqlTimezone = "GMT" . ( ( $matches[2] == 0 ) ? '' : ( ( $matches[1] ?: '+' ) . \intval( $matches[2] ) ) );
}
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->chartTimezoneInfo( $mysqlTimezone );
}
@@ -626,7 +574,16 @@ class _ajax extends \IPS\Dispatcher\Controller
\IPS\core\AdminNotification::dismissNotification( \IPS\Request::i()->id );
}
\IPS\Output::i()->json( array( 'status' => 'OK' ) );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'status' => 'OK' ) );
}
else
{
$ref = \IPS\Request::i()->referrer();
\IPS\Output::i()->redirect( $ref ?? \IPS\Http\Url::internal( '' ) );
}
}
/**
@@ -650,7 +607,7 @@ class _ajax extends \IPS\Dispatcher\Controller
try
{
$container = $containerClass::load( \IPS\Request::i()->container );
$container = $containerClass::load( (int) \IPS\Request::i()->container );
}
catch( \OutOfRangeException $e )
{
@@ -695,4 +652,19 @@ class _ajax extends \IPS\Dispatcher\Controller
\IPS\Output::i()->json( $results );
}
/**
* Return current CSRF token
*
* @return string|null
*/
public function getCsrfKey(): ?string
{
if ( ! \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->json( [ 'key' => \IPS\Session::i()->csrfKey ] );
}
return NULL;
}
}
@@ -97,7 +97,7 @@ class _announcement extends \IPS\Content\Controller
}
/* Make sure this is a local URL */
if( !$url->isInternal )
if( !( $url instanceof \IPS\Http\Url\Internal ) )
{
\IPS\Output::i()->json( array( 'status' => 'not_local' ) );
}
@@ -37,7 +37,7 @@ class _attachments extends \IPS\Dispatcher\Controller
/* Build Table */
$table = new \IPS\core\Attachments\Table( 'core_attachments', \IPS\Http\Url::internal( 'app=core&module=system&controller=attachments', 'front', 'attachments' ), array( array( 'attach_member_id=?', \IPS\Member::loggedIn()->member_id ) ) );
$table->include = array( 'attach_id', 'attach_location', 'attach_date', 'attach_file', 'attach_filesize', 'attach_is_image', 'attach_content', 'attach_hits' );
$table->include = array( 'attach_id', 'attach_location', 'attach_date', 'attach_file', 'attach_filesize', 'attach_is_image', 'attach_content', 'attach_hits', 'attach_security_key' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate('myAttachments'), 'rows' );
$table->joins = array();
@@ -51,7 +51,7 @@ class _editor extends \IPS\Dispatcher\Controller
protected function code()
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->code( \IPS\Request::i()->val, \IPS\Request::i()->editorId, md5( mt_rand() ), \IPS\Request::i()->lang ?: '' );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->code( htmlentities( \IPS\Request::i()->val, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8' ), \IPS\Request::i()->editorId, md5( mt_rand() ), \IPS\Request::i()->lang ?: '' );
}
/**
@@ -62,11 +62,20 @@ class _editor extends \IPS\Dispatcher\Controller
protected function image()
{
$maxImageDims = \IPS\Settings::i()->attachment_image_size ? explode( 'x', \IPS\Settings::i()->attachment_image_size ) : array( 1000, 750 );
$maxWidth = ( \IPS\Request::i()->actualWidth < $maxImageDims[0] ) ? \IPS\Request::i()->actualWidth : $maxImageDims[0];
$maxHeight = ( \IPS\Request::i()->actualHeight < $maxImageDims[1] ) ? \IPS\Request::i()->actualHeight : $maxImageDims[1];
$ratioH = round( \IPS\Request::i()->height / \IPS\Request::i()->width, 2 );
$ratioW = round( \IPS\Request::i()->width / \IPS\Request::i()->height, 2 );
if( \intval( $maxImageDims[0] ) === 0 && \intval( $maxImageDims[1] ) === 0 )
{
$maxWidth = \IPS\Request::i()->actualWidth;
$maxHeight = \IPS\Request::i()->actualHeight;
}
else
{
$maxWidth = ( \IPS\Request::i()->actualWidth < $maxImageDims[0] ) ? \IPS\Request::i()->actualWidth : $maxImageDims[0];
$maxHeight = ( \IPS\Request::i()->actualHeight < $maxImageDims[1] ) ? \IPS\Request::i()->actualHeight : $maxImageDims[1];
}
if ( \IPS\Request::i()->width > $maxWidth )
{
\IPS\Request::i()->width = $maxWidth;
@@ -115,13 +124,9 @@ class _editor extends \IPS\Dispatcher\Controller
{
try
{
if( \IPS\Request::i()->image )
{
\IPS\Text\Parser::isAllowedImageUrl( $url );
}
if ( \IPS\Request::i()->image and \IPS\Request::i()->width and \IPS\Request::i()->height )
{
\IPS\Text\Parser::isAllowedImageUrl( $url );
$embed = \IPS\Text\Parser::imageEmbed( $url, \intval( \IPS\Request::i()->width ), \intval( \IPS\Request::i()->height ) );
}
else
@@ -176,7 +181,7 @@ class _editor extends \IPS\Dispatcher\Controller
\IPS\Log::debug( $url . "\n" . $result['errorMessage'], 'embed_failure' );
}
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $result, \IPS\DateTime::create()->add( new\DateInterval( 'PT10S' ) ), TRUE );
\IPS\Data\Cache::i()->storeWithExpire( $cacheKey, $result, \IPS\DateTime::create()->add( new \DateInterval( 'PT10S' ) ), TRUE );
\IPS\Output::i()->json( $result );
}
@@ -426,6 +431,11 @@ class _editor extends \IPS\Dispatcher\Controller
'title' => $row['title']
);
}
if ( empty( $results['images'] ) )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ) );
}
\IPS\Output::i()->json( $results );
}
@@ -34,18 +34,13 @@ class _login extends \IPS\Dispatcher\Controller
*/
protected function manage()
{
/* Did we just log in? */
if ( \IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->_fromLogin ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
}
/* Init login class */
$login = new \IPS\Login( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' ) );
/* What's our referrer? */
$postBeforeRegister = NULL;
$ref = \IPS\Request::i()->ref;
$ref = \IPS\Request::i()->referrer( FALSE, FALSE );
if ( !$ref and isset( \IPS\Request::i()->cookie['post_before_register'] ) )
{
try
@@ -111,12 +106,18 @@ class _login extends \IPS\Dispatcher\Controller
$error = $e->getMessage();
}
/* Are we already logged in? */
if ( \IPS\Member::loggedIn()->member_id AND ( !\IPS\Request::i()->_err OR \IPS\Request::i()->_err != 'login_as_user_login' ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('') );
}
/* Display Login Form */
\IPS\Output::i()->allowDefaultWidgets = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('login');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->login( $login, $ref, $error );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->login( $login, base64_encode( $ref ), $error );
/* Don't cache for a short while to ensure sessions work */
\IPS\Request::i()->setCookie( 'noCache', 1 );
@@ -290,7 +291,7 @@ class _login extends \IPS\Dispatcher\Controller
*/
protected function loginas()
{
if ( !\IPS\Request::i()->key or \IPS\Data\Store::i()->admin_login_as_user != \IPS\Request::i()->key )
if ( !\IPS\Request::i()->key or !\IPS\Login::compareHashes( (string) \IPS\Data\Store::i()->admin_login_as_user, (string) \IPS\Request::i()->key ) )
{
\IPS\Output::i()->error( 'invalid_login_as_user_key', '3S167/1', 403, '' );
}
@@ -56,18 +56,29 @@ class _lostpass extends \IPS\Dispatcher\Controller
{
/* Build the form */
$form = new \IPS\Helpers\Form( "lostpass", 'request_password' );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ), function( $val ){
if( !\IPS\Login::emailIsInUse( $val ) )
{
throw new \LogicException( 'lost_pass_no_email' );
}
}) );
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
$captcha = new \IPS\Helpers\Form\Captcha;
if ( (string) $captcha !== '' )
{
$form->add( $captcha );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('lost_password');
/* Handle the reset */
if ( $values = $form->values() )
{
if( !\IPS\Login::emailIsInUse( $values['email_address'] ) )
{
/* We intentionally show the same message as if the request was successful to avoid leaking information about membership */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->lostPassConfirm( 'lost_pass_confirm' );
return;
}
/* If using "normal" method and we have an account, and at least one login handler we can process a password change for, we're good */
$member = \IPS\Member::load( $values['email_address'], 'email' );
if ( $member->member_id and \IPS\Settings::i()->allow_forgot_password == 'normal' )
@@ -42,19 +42,21 @@ class _marketplace extends \IPS\Dispatcher\Controller
\IPS\Db::i()->update( 'core_marketplace_tokens', array( 'token' => \IPS\Request::i()->access_token, 'expires_at' => time() + \IPS\Request::i()->expires_in ), array( 'id=?', \IPS\Request::i()->member ) );
echo 'OK';
exit;
$response = 'OK';
$responseCode = 200;
}
catch ( \UnderflowException $e )
{
echo 'BAD_HASH';
exit;
$response = 'BAD_HASH';
$responseCode = 403;
}
}
else
{
echo 'NO_MEMBER';
exit;
$response = 'NO_MEMBER';
$responseCode = 404;
}
\IPS\Output::i()->sendOutput( $response, $responseCode, 'text/plain' );
}
}
@@ -65,12 +65,84 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notifications');
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Output::i()->title );
\IPS\Output::i()->output = (string) $table;
}
}
/**
* Subscribe a user's device to push notifications
*
* @return void
*/
protected function subscribeToPush()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
if ( isset( \IPS\Request::i()->subscription ) AND $subscription = json_decode( \IPS\Request::i()->subscription, TRUE ) )
{
$device = \IPS\Member\Device::loadOrCreate( \IPS\Member::loggedIn(), FALSE );
/* If a subscription already exists, then just return */
try
{
\IPS\Db::i()->select( '*', 'core_notifications_pwa_keys', array( "`member`=? AND p256dh=? AND auth=?", \IPS\Member::loggedIn()->member_id, $subscription['keys']['p256dh'], $subscription['keys']['auth'] ) )->first();
/* Make sure encoding and device is up to date, though since it's needed for encryption transfer. */
\IPS\Db::i()->update( 'core_notifications_pwa_keys', array(
'encoding' => \IPS\Request::i()->encoding,
'device' => $device->device_key
), array( "`member`=? AND p256dh=? AND auth=?", \IPS\Member::loggedIn()->member_id, $subscription['keys']['p256dh'], $subscription['keys']['auth'] ) );
}
catch( \UnderflowException $e )
{
\IPS\Db::i()->insert( 'core_notifications_pwa_keys', array(
'member' => \IPS\Member::loggedIn()->member_id,
'endpoint' => $subscription['endpoint'],
'p256dh' => $subscription['keys']['p256dh'],
'auth' => $subscription['keys']['auth'],
'encoding' => \IPS\Request::i()->encoding,
'device' => $device->device_key
) );
}
\IPS\Output::i()->json( 'OK' );
}
else
{
\IPS\Output::i()->error( 'invalid_push_subscription', '2C154/H', 403, '' );
}
}
/**
* Verify a subscription with the provided key exists for the logged-in user
*
* @return void
*/
protected function verifySubscription()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
if ( isset( \IPS\Request::i()->key ) )
{
/* See if a subscription already exists */
try
{
\IPS\Db::i()->select( '*', 'core_notifications_pwa_keys', array( "`member`=? AND p256dh=?", \IPS\Member::loggedIn()->member_id, \IPS\Request::i()->key ) )->first();
\IPS\Output::i()->json( 'OK' );
}
catch( \UnderflowException $e )
{
// Just let it return the error below
}
}
\IPS\Output::i()->error( 'invalid_push_subscription', '2C154/I', 403, '' );
}
/**
* Options: Dispatcher
*
@@ -127,8 +199,7 @@ class _notifications extends \IPS\Dispatcher\Controller
if ( $form === TRUE )
{
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Member::loggedIn()->notificationsConfiguration = NULL;
{
$categories = \IPS\Notification::membersOptionCategories( \IPS\Member::loggedIn(), array( $extensionKey => $extension ) );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate('system')->notificationSettingsIndexRowDetails( $extensionKey, $categories[ $extensionKey ] ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
@@ -153,30 +224,6 @@ class _notifications extends \IPS\Dispatcher\Controller
}
}
/**
* Enable/Disable Sounds
*
* @param array $extensions The extensions
* @return void
*/
protected function sounds()
{
$this->_checkLoggedIn();
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->members_bitoptions['disable_notification_sounds'] = ( \IPS\Request::i()->enable ? false : true );
\IPS\Member::loggedIn()->save();
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'ok' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=notifications&do=options', 'front', 'notifications_options' ) );
}
}
/**
* Stop receiving all notifications to a particular method
*
@@ -219,6 +266,11 @@ class _notifications extends \IPS\Dispatcher\Controller
$extension::disableExtra( \IPS\Member::loggedIn(), \IPS\Request::i()->type );
}
}
if ( \IPS\Request::i()->type === 'push' )
{
\IPS\Member::loggedIn()->clearPwaAuths();
}
if ( \IPS\Request::i()->isAjax() )
{
@@ -239,9 +291,18 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$this->_checkLoggedIn();
try
{
$application = \IPS\Application::load( \IPS\Request::i()->follow_app );
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'error_no_app', '3C154/F', 404, '' );
}
/* Get class */
$class = NULL;
foreach ( \IPS\Application::load( \IPS\Request::i()->follow_app )->extensions( 'core', 'ContentRouter' ) as $ext )
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $ext )
{
foreach ( $ext->classes as $classname )
{
@@ -309,7 +370,7 @@ class _notifications extends \IPS\Dispatcher\Controller
{
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
/* Set navigation */
@@ -325,7 +386,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
elseif ( $class == 'IPS\Member\Club' )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->_title ) ) );
\IPS\Output::i()->breadcrumb = array(
array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') ),
@@ -333,8 +394,13 @@ class _notifications extends \IPS\Dispatcher\Controller
);
}
elseif ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
{
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) )
{
throw new \OutOfRangeException;
}
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'follow_thing', FALSE, array( 'sprintf' => array( $thing->mapped('title') ) ) );
/* Set navigation */
@@ -355,7 +421,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('follow_thing', FALSE, array( 'sprintf' => array( $thing->name ) ) );
@@ -535,10 +601,28 @@ class _notifications extends \IPS\Dispatcher\Controller
/* Send notification if following member */
if( $class == "IPS\Member" )
{
/* Give points */
$receiver = \IPS\Member::load( \IPS\Request::i()->follow_rel_id );
$receiver->achievementAction( 'core', 'FollowMember', [
'giver' => \IPS\Member::loggedIn()
] );
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'member_follow', \IPS\Member::loggedIn(), array( \IPS\Member::loggedIn() ) );
$notification->recipients->attach( $thing );
$notification->send();
}
else if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
{
\IPS\Member::loggedIn()->achievementAction( 'core', 'FollowNode', $class::load( \IPS\Request::i()->follow_id ) );
}
else if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$item = $class::load( \IPS\Request::i()->follow_id );
\IPS\Member::loggedIn()->achievementAction( 'core', 'FollowContentItem', [
'item' => $item,
'author' => $item->author()
] );
}
/* Boink */
if ( \IPS\Request::i()->isAjax() )
@@ -597,7 +681,7 @@ class _notifications extends \IPS\Dispatcher\Controller
try
{
$thing = $class::loadAndCheckPerms( $follow['follow_rel_id'] );
$thing = $class::loadAndCheckPerms( (int) $follow['follow_rel_id'] );
foreach ( $thing->nodes() as $node )
{
@@ -651,7 +735,7 @@ class _notifications extends \IPS\Dispatcher\Controller
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
}
if ( !class_exists( $class ) )
if ( !class_exists( $class ) or !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
\IPS\Output::i()->error( 'node_error', '2C154/5', 404, '' );
}
@@ -665,7 +749,7 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $containerClass::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $classname::containerFollowers( $thing, $classname::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $classname::containerFollowerCount( $thing );
$anonymous = $classname::containerFollowerCount( $thing, $classname::FOLLOW_ANONYMOUS );
@@ -673,7 +757,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else if ( $class == "IPS\Member\Club" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -681,7 +765,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else if ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -689,7 +773,7 @@ class _notifications extends \IPS\Dispatcher\Controller
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$followers = $thing->followers( $class::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( ( $thisPage - 1 ) * $perPage, $perPage ), 'name' );
$followersCount = $thing->followersCount();
$anonymous = $thing->followersCount( $class::FOLLOW_ANONYMOUS );
@@ -735,7 +819,7 @@ class _notifications extends \IPS\Dispatcher\Controller
* @return void
*/
protected function unfollowFromEmail()
{
{
/* Logged in? */
if ( \IPS\Member::loggedIn()->member_id )
{
@@ -764,6 +848,11 @@ class _notifications extends \IPS\Dispatcher\Controller
throw new \Exception;
}
if( !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
throw new \Exception;
}
/* Get class */
if( \IPS\Request::i()->follow_app == 'core' and \IPS\Request::i()->follow_area == 'member' )
{
@@ -776,6 +865,11 @@ class _notifications extends \IPS\Dispatcher\Controller
else
{
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) )
{
throw new \Exception;
}
}
if ( !class_exists( $class ) )
{
@@ -789,22 +883,22 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::load( \IPS\Request::i()->follow_id );
$thing = $containerClass::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->_title;
}
else if ( $class == "IPS\Member\Club" )
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->_title;
}
else if ( $class != "IPS\Member" )
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->mapped('title');
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$title = $thing->name;
}
@@ -890,7 +984,8 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$class = 'IPS\\' . \IPS\Request::i()->follow_app . '\\' . mb_ucfirst( \IPS\Request::i()->follow_area );
}
if ( !class_exists( $class ) )
if ( !class_exists( $class ) or !array_key_exists( \IPS\Request::i()->follow_app, \IPS\Application::applications() ) )
{
\IPS\Output::i()->error( 'node_error', '2C154/5', 404, '' );
}
@@ -903,17 +998,22 @@ class _notifications extends \IPS\Dispatcher\Controller
{
$classname = $class::$contentItemClass;
$containerClass = $class;
$thing = $containerClass::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $containerClass::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$count = $classname::containerFollowerCount( $thing );
}
else if ( $class != "IPS\Member" )
{
$thing = $class::loadAndCheckPerms( \IPS\Request::i()->follow_id );
$thing = $class::loadAndCheckPerms( (int) \IPS\Request::i()->follow_id );
$count = $thing->followersCount();
}
else
{
$thing = $class::load( \IPS\Request::i()->follow_id );
if( !is_subclass_of( $class, "\IPS\Content\Followable" ) AND $class != "IPS\Member" )
{
\IPS\Output::i()->error( 'node_error', '2C154/J', 404, '' );
}
$thing = $class::load( (int) \IPS\Request::i()->follow_id );
$count = $thing->followersCount();
}
}
@@ -983,4 +1083,39 @@ class _notifications extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Request::i()->referrer() ?: \IPS\Http\Url::internal( '' ), 'followers_removed' );
}
/**
* Retrieve notification data and return it to the service worker
*
* @return void
*/
protected function fetchNotification()
{
/* Got the ID? */
if( !\IPS\Request::i()->id )
{
\IPS\Output::i()->json( array( 'error' => 'missing_id' ), 404 );
}
/* Got the notification? */
try
{
$notification = \IPS\Db::i()->select( '*', 'core_notifications_pwa_queue', array( 'id=?', \IPS\Request::i()->id ) )->first();
}
catch( \UnderflowException $e )
{
\IPS\Output::i()->json( array( 'error' => 'not_found' ), 404 );
}
/* Is this our notification? */
$data = json_decode( $notification['notification_data'], true );
if( !isset( $data['member'] ) OR $data['member'] != \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->json( array( 'error' => 'no_permission' ), 403 );
}
/* Send the data */
\IPS\Output::i()->json( $data );
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @brief Offline page output
* @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 12 Feb 2021
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Offline page Controller
*/
class _offline extends \IPS\Dispatcher\Controller
{
/**
* View Notifications
*
* @return void
*/
protected function manage()
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->offline(), 200, 'text/html' );
}
}
@@ -56,7 +56,8 @@ class _pixabay extends \IPS\Dispatcher\Controller
'key' => \IPS\Settings::i()->pixabay_apikey,
'image_type' => 'photo',
'per_page' => 20,
'page' => ( $offset ) ? ceil( $offset / 20 ) + 1 : 1
'page' => ( $offset ) ? ceil( $offset / 20 ) + 1 : 1,
'safesearch' => ( \IPS\Settings::i()->pixabay_safesearch ) ? 'true' : 'false',
);
$parameters['q'] = urlencode( $query );
@@ -79,6 +80,7 @@ class _pixabay extends \IPS\Dispatcher\Controller
\IPS\Output::i()->json( array('error' => $request['message'] ) );
}
$results = array( 'pagination' => array( 'total_count' => $request['total'] ) );
foreach ( $request['hits'] as $row )
{
@@ -89,6 +91,11 @@ class _pixabay extends \IPS\Dispatcher\Controller
);
}
if ( empty( $results['images'] ) )
{
\IPS\Output::i()->json( array( 'error' => \IPS\Theme::i()->getTemplate( 'system', 'core' )->noResults() ) );
}
\IPS\Output::i()->json( $results );
}
}
@@ -34,6 +34,18 @@ class _privacy extends \IPS\Dispatcher\Controller
\IPS\Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_set_to_none_acp' );
}
if ( \IPS\Settings::i()->privacy_type == "external" )
{
if ( $url = \IPS\Settings::i()->privacy_link )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $url ) );
}
else
{
\IPS\Output::i()->error( 'node_error', '2C381/1', 404, 'privacy_link_not_set_acp' );
}
}
$subprocessors = array();
/* Work out the main subprocessors that the user has no direct choice over */
if ( \IPS\Settings::i()->privacy_show_processors )
@@ -39,7 +39,7 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Settings::i()->base_url );
}
if( \IPS\Request::i()->do !== 'complete'
if( \IPS\Request::i()->do !== 'complete' and \IPS\Request::i()->do !== 'setPassword'
and \IPS\Request::i()->do !== 'changeEmail' and \IPS\Request::i()->do !== 'validate'
and \IPS\Request::i()->do !== 'validating' and \IPS\Request::i()->do !== 'reconfirm'
and \IPS\Request::i()->do !== 'finish' and \IPS\Request::i()->do !== 'cancel' )
@@ -60,6 +60,7 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimalNoHome';
}
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( 'app=core&module=system&controller=register', 'front', 'register' );
}
/**
@@ -524,7 +525,7 @@ class _register extends \IPS\Dispatcher\Controller
* @param \IPS\Helpers\Form $form The form object
* @return \IPS\Member
*/
public static function _createMember( $values, $profileFields, $postBeforeRegister = NULL, &$form )
public static function _createMember( $values, $profileFields, $postBeforeRegister, &$form )
{
/* Create */
$member = new \IPS\Member;
@@ -1003,7 +1004,7 @@ class _register extends \IPS\Dispatcher\Controller
}
catch( \BadMethodCallException $e ) {}
}
\IPS\Member::loggedIn()->memberSync( 'onEmailChange', array( $values['new_email'], $oldEmail ) );
\IPS\Member::loggedIn()->logHistory( 'core', 'email_change', array( 'old' => $oldEmail, 'new' => $values['new_email'], 'by' => 'manual' ) );
/* Invalidate sessions except this one */
@@ -1037,6 +1038,10 @@ class _register extends \IPS\Dispatcher\Controller
\IPS\Email::buildFromTemplate( 'core', $pending['email_chg'] ? 'email_change' : 'registration_validate', array( \IPS\Member::loggedIn(), $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
}
else
{
\IPS\Member::loggedIn()->memberSync( 'onEmailChange', array( $values['new_email'], $oldEmail ) );
}
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
@@ -1087,10 +1092,7 @@ class _register extends \IPS\Dispatcher\Controller
/* Generate form */
$form = new \IPS\Helpers\Form( 'reconfirm_checkbox', 'reconfirm_checkbox' );
if ( isset( \IPS\Request::i()->ref ) )
{
$form->hiddenValues['ref'] = \IPS\Request::i()->ref;
}
$form->hiddenValues['ref'] = base64_encode( \IPS\Request::i()->referrer( FALSE, FALSE, 'front' ) ?? \IPS\Http\Url::baseUrl() );
$form->class = 'ipsForm_vertical';
/* Handle submissions */
@@ -1250,7 +1252,7 @@ class _register extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function setPassword(): void
public function setPassword()
{
try
{
@@ -0,0 +1,85 @@
<?php
/**
* @brief Service worker output
* @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 12 Feb 2021
*/
namespace IPS\core\modules\front\system;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Service worker controller
*/
class _serviceworker extends \IPS\Dispatcher\Controller
{
/**
* View Notifications
*
* @return void
*/
protected function manage()
{
$cachedUrls = array();
$cachedUrls[] = (string) \IPS\Http\Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
$notificationIcon = NULL;
/* Get an icon to use in notifications */
$homeScreenIcons = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
if( \count( $homeScreenIcons ) )
{
foreach( $homeScreenIcons as $name => $image )
{
if( isset( $image['width'] ) and $image['width'] == 192 )
{
$notificationIcon = \IPS\File::get( 'core_Icons', $image['url'] )->url;
break;
}
}
}
/* VARIABLES TO PASS THROUGH TO JS */
$DEBUG = ( ( \IPS\IN_DEV and \IPS\DEV_DEBUG_JS ) or \IPS\DEBUG_JS ) ? 'true' : 'false'; // Weird casting is intentional.
$SW_CACHE_BUST = \IPS\SW_CACHE_BUST ? 'true' : 'false'; // Weird casting is intentional.
$HTTP_CACHE_DURATION = \IPS\CACHE_PAGE_TIMEOUT;
$BASE_URL = \IPS\Settings::i()->base_url;
$CACHED_ASSETS = json_encode( $cachedUrls, JSON_UNESCAPED_SLASHES );
$OFFLINE_URL = (string) \IPS\Http\Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
$CACHE_VERSION = \IPS\Theme::i()->cssCacheBustKey();
$NOTIFICATION_ICON = $notificationIcon ? "\"{$notificationIcon}\"" : 'null'; // Weird casting is intentional. 'null' will become literal null in JS file.
$DEFAULT_NOTIFICATION_TITLE = \IPS\Member::loggedIn()->language()->addToStack('default_notification_title');
$DEFAULT_NOTIFICATION_BODY = \IPS\Member::loggedIn()->language()->addToStack('default_notification_body');
$output = <<<JAVASCRIPT
"use strict";
const DEBUG = {$DEBUG};
const SW_CACHE_BUST = {$SW_CACHE_BUST};
const BASE_URL = "{$BASE_URL}";
const CACHED_ASSETS = {$CACHED_ASSETS};
const CACHE_NAME = 'invision-community-{$CACHE_VERSION}';
const HTTP_CACHE_DURATION = {$HTTP_CACHE_DURATION};
const HTTP_CACHE_BUFFER = 60;
const OFFLINE_URL = "{$OFFLINE_URL}";
const NOTIFICATION_ICON = {$NOTIFICATION_ICON};
const DEFAULT_NOTIFICATION_TITLE = "{$DEFAULT_NOTIFICATION_TITLE}";
const DEFAULT_NOTIFICATION_BODY = "{$DEFAULT_NOTIFICATION_BODY}";
JAVASCRIPT;
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
$output .= file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/js/serviceWorker.js' );
$cacheHeaders = \IPS\IN_DEV !== true ? \IPS\Output::getCacheHeaders(time(), 86400) : array();
\IPS\Output::i()->sendOutput($output, 200, 'text/javascript', $cacheHeaders);
}
}
@@ -350,6 +350,22 @@ class _settings extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->error( 'no_module_permission', '2C122/T', 403, '' );
}
$canChangePassword = FALSE;
foreach ( \IPS\Login::methods() as $method )
{
if ( $method->canChangePassword( \IPS\Member::loggedIn() ) )
{
$canChangePassword = TRUE;
break;
}
}
if( !$canChangePassword )
{
\IPS\Output::i()->error( 'no_module_permission', '3C122/W', 403, '' );
}
if( \IPS\Member::loggedIn()->isAdmin() )
{
@@ -538,7 +554,7 @@ class _settings extends \IPS\Dispatcher\Controller
}
}
$oauthApps = \IPS\Db::i()->select( 'COUNT(DISTINCT client_id)', 'core_oauth_server_access_tokens', array( 'member_id=? AND oauth_enabled=1 AND oauth_ucp=1', \IPS\Member::loggedIn()->member_id ) )
$oauthApps = \IPS\Db::i()->select( 'COUNT(DISTINCT client_id)', 'core_oauth_server_access_tokens', array( "member_id=? AND oauth_enabled=1 AND oauth_ucp=1 AND status='active'", \IPS\Member::loggedIn()->member_id ) )
->join( 'core_oauth_clients', 'oauth_client_id=client_id' )
->first();
@@ -563,7 +579,7 @@ class _settings extends \IPS\Dispatcher\Controller
$device->login_key = NULL;
$device->save();
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', $device->member_id, $device->device_key ) );
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'member_id=? AND device_key=?', $device->member_id, $device->device_key ) );
\IPS\Member::loggedIn()->logHistory( 'core', 'login', array( 'type' => 'logout', 'device' => $device->device_key ) );
@@ -585,20 +601,27 @@ class _settings extends \IPS\Dispatcher\Controller
if ( !isset( $_SESSION['passwordValidatedForMfa'] ) )
{
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ), \IPS\Login::LOGIN_REAUTHENTICATE );
$error = NULL;
try
$usernamePasswordMethods = $login->usernamePasswordMethods();
$buttonMethods = $login->buttonMethods();
/* Only prompt for re-authentication if it is possible */
if( $usernamePasswordMethods OR $buttonMethods )
{
if ( $success = $login->authenticate() )
$error = NULL;
try
{
$_SESSION['passwordValidatedForMfa'] = TRUE;
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
if ( $success = $login->authenticate() )
{
$_SESSION['passwordValidatedForMfa'] = TRUE;
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
}
}
catch ( \IPS\Login\Exception $e )
{
$error = $e->getMessage();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsMfaPassword( $login, $error );
}
catch ( \IPS\Login\Exception $e )
{
$error = $e->getMessage();
}
return \IPS\Theme::i()->getTemplate( 'system' )->settingsMfaPassword( $login, $error );
}
/* Get our handlers and the output, even if it's just for a backdrop */
@@ -742,7 +765,7 @@ class _settings extends \IPS\Dispatcher\Controller
$this->_performRedirect( \IPS\Http\Url::internal('') );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_complete_details');
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reg_complete_2fa_title');
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( '2fa.css', 'core', 'global' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->mfaSetup( $handlers, \IPS\Member::loggedIn(), \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&do=initialMfa', 'front', 'settings' )->addRef( $this->_performRedirect( \IPS\Http\Url::internal(''), '', TRUE ) ) );
}
@@ -1128,7 +1151,7 @@ class _settings extends \IPS\Dispatcher\Controller
}
/* Number of Images */
if ( \is_numeric( $sigLimits[1] ) and $signature->getElementsByTagName('img')->length > 0 )
if ( \is_numeric( $sigLimits[1] ) )
{
$imageCount = 0;
foreach ( $signature->getElementsByTagName('img') as $img )
@@ -1138,6 +1161,15 @@ class _settings extends \IPS\Dispatcher\Controller
$imageCount++;
}
}
/* Look for background-image URLs too */
$xpath = new \DOMXpath( $signature );
foreach ( $xpath->query("//*[contains(@style, 'url') and contains(@style, 'background')]") as $styleUrl )
{
$imageCount++;
}
if( $imageCount > $sigLimits[1] )
{
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_images_exceeded');
@@ -1479,6 +1511,10 @@ class _settings extends \IPS\Dispatcher\Controller
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ), 'issued DESC' ) as $accessToken )
{
if ( $accessToken['status'] == 'revoked' )
{
continue;
}
try
{
$client = \IPS\Api\OAuthClient::load( $accessToken['client_id'] );
@@ -1538,7 +1574,7 @@ class _settings extends \IPS\Dispatcher\Controller
try
{
$client = \IPS\Api\OAuthClient::load( \IPS\Request::i()->client_id );
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=? AND member_id=?', $client->client_id, \IPS\Member::loggedIn()->member_id ) );
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'client_id=? AND member_id=?', $client->client_id, \IPS\Member::loggedIn()->member_id ) );
\IPS\Member::loggedIn()->logHistory( 'core', 'oauth', array( 'type' => 'revoked_access_token', 'client' => $client->client_id ) );
}
catch ( \Exception $e ) { }
@@ -280,6 +280,8 @@ class _warnings extends \IPS\Content\Controller
'remove' => $remove,
'remove_override' => TRUE,
'notes' => NULL,
'cheev_point_reduction' => 0,
'cheev_override' => TRUE
) );
}
@@ -313,6 +315,8 @@ class _warnings extends \IPS\Content\Controller
'remove' => $remove,
'remove_override' => $reason->remove_override,
'notes' => $reason->notes,
'cheev_point_reduction' => $reason->cheev_point_reduction,
'cheev_override' => $reason->cheev_override
) );
}
@@ -324,6 +328,8 @@ class _warnings extends \IPS\Content\Controller
'remove' => $remove,
'remove_override' => FALSE,
'notes' => NULL,
'cheev_point_reduction' => 0,
'cheev_override' => FALSE
) );
}
}