Version 4.6.0
This commit is contained in:
1 parent
f79dcf067a
commit
517a5e1f70
2036 files changed
+110041
-26162
No files matched your search
@@ -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
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user