Version 4.7.13
This commit is contained in:
1 parent
fd22d99e69
commit
7124a02564
954 files changed
+29277
-59308
No files matched your search
@@ -397,6 +397,13 @@ class _directory extends \IPS\Dispatcher\Controller
|
||||
$club = new \IPS\Member\Club;
|
||||
if ( $form = $club->form( FALSE, TRUE, $availableTypes ) )
|
||||
{
|
||||
if( $values = $form->values() )
|
||||
{
|
||||
$club->processForm( $values, FALSE, TRUE, $availableTypes );
|
||||
|
||||
\IPS\Output::i()->redirect( $club->url() );
|
||||
}
|
||||
|
||||
$form->class = 'ipsForm_vertical';
|
||||
}
|
||||
else
|
||||
|
||||
@@ -77,9 +77,10 @@ class _view extends \IPS\Helpers\CoverPhoto\Controller
|
||||
array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') )
|
||||
);
|
||||
|
||||
/* Add a "Search in this club" contextual search option */
|
||||
/* Add a "Search in this club" contextual search option and set to default*/
|
||||
\IPS\Output::i()->contextualSearchOptions[ \IPS\Member::loggedIn()->language()->addToStack( 'search_contextual_item_club' ) ] = array( 'type' => '', 'club' => "c{$this->club->id}" );
|
||||
|
||||
\IPS\Output::i()->defaultSearchOption = array( '', 'search_contextual_item_club' );
|
||||
|
||||
/* CSS */
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/clubs.css', 'core', 'front' ) );
|
||||
if ( \IPS\Theme::i()->settings['responsive'] )
|
||||
@@ -265,22 +266,42 @@ class _view extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C350/A', 403, '' );
|
||||
}
|
||||
|
||||
if ( $form = $this->club->form() )
|
||||
$form = $this->club->form();
|
||||
|
||||
if( $values = $form->values() )
|
||||
{
|
||||
\IPS\Output::i()->title = $this->club->name;
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
$this->club->skipCloneDuplication = TRUE;
|
||||
$old = clone $this->club;
|
||||
|
||||
$this->club->processForm( $values, FALSE, FALSE, NULL );
|
||||
|
||||
$changes = $this->club::renewalChanges( $old, $this->club );
|
||||
|
||||
if ( !empty( $changes ) )
|
||||
{
|
||||
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->decision( 'product_change_blurb', array(
|
||||
'product_change_blurb_existing' => \IPS\Http\Url::internal( "app=core&module=clubs&controller=view&do=updateExisting&id={$this->club->id}" )->setQueryString( 'changes', json_encode( $changes ) )->csrf(),
|
||||
'product_change_blurb_new' => $this->club->url(),
|
||||
) );
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->output = $form;
|
||||
\IPS\Output::i()->redirect( $this->club->url() );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = $this->club->name;
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( $this->club->url() );
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1776,4 +1797,27 @@ class _view extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->metaTags['og:description'] = $this->club->about;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Existing Purchases
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateExisting()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Make sure logged-in user has permission */
|
||||
if ( !( $this->club->owner and $this->club->owner->member_id == \IPS\Member::loggedIn()->member_id ) and !\IPS\Member::loggedIn()->modPermission('can_access_all_clubs') )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '3C350/Q', 403, '' );
|
||||
}
|
||||
|
||||
$changes = json_decode( \IPS\Request::i()->changes, TRUE );
|
||||
|
||||
\IPS\Task::queue( 'core', 'UpdateClubRenewals', array( 'changes' => $changes, 'club' => $this->club->id ), 5 );
|
||||
|
||||
/* Redirect */
|
||||
\IPS\Output::i()->redirect( $this->club->url(), 'saved' );
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ class _contact extends \IPS\Dispatcher\Controller
|
||||
/* Execute */
|
||||
return parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method
|
||||
*
|
||||
@@ -82,6 +83,28 @@ class _contact extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->member_id AND \IPS\Settings::i()->contact_email_verify )
|
||||
{
|
||||
$key = \IPS\Login::generateRandomString( 32 );
|
||||
\IPS\Db::i()->insert( 'core_contact_verify', array(
|
||||
'email_address' => $values['email_address'],
|
||||
'contact_data' => json_encode( $values ),
|
||||
'verify_key' => $key
|
||||
), TRUE );
|
||||
|
||||
$email = \IPS\Email::buildFromTemplate( 'core', 'contact_verify', array( $values['email_address'], $key ), \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
$email->send( $values['email_address'] );
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json( 'OK' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact_verify' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactVerify();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $extensions as $k => $class )
|
||||
{
|
||||
if ( $handled = $class->handleForm( $values ) )
|
||||
@@ -102,7 +125,63 @@ class _contact extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contact( $form );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function confirm()
|
||||
{
|
||||
/* Show interstitial page to prevent email clients from auto-verifying. */
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', NULL );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$form->actionButtons[] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( 'contact_click_to_verify', 'submit', null, 'ipsButton ipsButton_primary ipsButton_fullWidth', array( 'tabindex' => '2', 'accesskey' => 's' ) );
|
||||
$form->hiddenValues['email'] = \IPS\Request::i()->email;
|
||||
$form->hiddenValues['key'] = \IPS\Request::i()->key;
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$verify = \IPS\Db::i()->select( '*', 'core_contact_verify', array( "email_address=?", $values['email'] ) )->first();
|
||||
}
|
||||
catch( \UnderflowException )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C435/1', 404, '' );
|
||||
}
|
||||
|
||||
if ( \IPS\Login::compareHashes( $verify['verify_key'], $values['key'] ) === FALSE )
|
||||
{
|
||||
\IPS\Output::i()->error( 'contact_verify_key_mismatch', '2C435/2', 403, '' );
|
||||
}
|
||||
|
||||
/* Send it */
|
||||
$extensions = \IPS\Application::allExtensions( 'core', 'ContactUs', FALSE, 'core', 'InternalEmail', TRUE );
|
||||
|
||||
foreach( $extensions AS $k => $extension )
|
||||
{
|
||||
if ( $extension->handleForm( json_decode( $verify['contact_data'], true ) ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_contact_verify', array( "email_address=?", $values['email'] ) );
|
||||
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'message_sent' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactDone();
|
||||
return;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'contact_verify' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->contactConfirmVerify( $form );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief account
|
||||
* @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 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\front\hive;
|
||||
|
||||
use IPS\core\Hive;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* account
|
||||
*/
|
||||
class _account extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/2', 404, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow Community
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function follow()
|
||||
{
|
||||
/* Check Hive is Enabled */
|
||||
if( empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/3', 404, '' );
|
||||
}
|
||||
|
||||
if( Member::loggedIn()->member_id )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'hive', 'hive_subscribe_button' );
|
||||
$form->class = 'ipsForm_vertical ipsType_center ipsForm_noLabels';
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'hive_subscriber_email_address', Member::loggedIn()->email ?? '', TRUE, [ 'placeholder' => Member::loggedIn()->language()->addToStack('email_address')] ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
Session::i()->csrfCheck();
|
||||
|
||||
try
|
||||
{
|
||||
$response = Hive::api( 'subscribe', [
|
||||
'site_member_id' => Member::loggedIn()->member_id ?? 0,
|
||||
'group_hash' => Member::loggedin()->member_id ? Hive::groupHash( Member::loggedin() ) : 'guest',
|
||||
'member_email' => $values['hive_subscriber_email_address'],
|
||||
] );
|
||||
|
||||
/* Save subscribe */
|
||||
if ( Member::loggedIn()->member_id )
|
||||
{
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_hive_subscribers', [ 'member_id' => Member::loggedIn()->member_id, 'subscribe_date' => time() ] );
|
||||
}
|
||||
catch ( Db\Exception $e ){}
|
||||
}
|
||||
|
||||
if ( !empty( $response['redirect_url'] ) )
|
||||
{
|
||||
Output::i()->redirect( Url::external( $response['redirect_url'] ) );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception|\LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$form = Theme::i()->getTemplate( 'hive', 'core', 'front' )->signin();
|
||||
}
|
||||
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('hive_subscribe_to_hive', NULL, [ 'sprintf' => Settings::i()->board_name ]);
|
||||
Output::i()->output = Theme::i()->getTemplate( 'hive', 'core', 'front' )->follow( $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymously follow community
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function anonymousFollow()
|
||||
{
|
||||
Output::i()->pageCaching = FALSE;
|
||||
|
||||
/* Check Hive is Enabled */
|
||||
if( empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/4', 404, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$response = Hive::api( 'subscribe', [
|
||||
'site_member_id' => 0,
|
||||
'group_hash' => 'guest',
|
||||
'member_email' => false,
|
||||
] );
|
||||
|
||||
/* Save subscribe */
|
||||
if ( !empty( $response['redirect_url'] ) )
|
||||
{
|
||||
Output::i()->redirect( Url::external( $response['redirect_url'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \LogicException( 'unknown_error' );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception|\LogicException $e )
|
||||
{
|
||||
Output::i()->error( $e->getMessage(), '2HV100/1', 403, '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief content
|
||||
* @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 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\front\hive;
|
||||
|
||||
use IPS\core\Hive;
|
||||
use IPS\Content\Item;
|
||||
use IPS\core\Stream;
|
||||
use IPS\Db;
|
||||
use IPS\File;
|
||||
use IPS\File\Exception;
|
||||
use IPS\Http\Url;
|
||||
use IPS\IPS;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use Jose\Component\Checker\AudienceChecker;
|
||||
use Jose\Component\Checker\ClaimCheckerManager;
|
||||
use Jose\Component\Checker\ExpirationTimeChecker;
|
||||
use Jose\Component\Checker\InvalidClaimException;
|
||||
use Jose\Component\Checker\IssuedAtChecker;
|
||||
use Jose\Component\Checker\IssuerChecker;
|
||||
use Jose\Component\Checker\MissingMandatoryClaimException;
|
||||
use Jose\Component\Checker\NotBeforeChecker;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* content
|
||||
*/
|
||||
class _content extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
public function __construct( $url = NULL )
|
||||
{
|
||||
parent::__construct( $url );
|
||||
|
||||
IPS::$PSR0Namespaces['Jose'] = \IPS\ROOT_PATH . '/system/3rd_party/JwtFramework/src';
|
||||
IPS::$PSR0Namespaces['Base64Url'] = \IPS\ROOT_PATH .'/system/3rd_party/Base64Url';
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if( isset( Request::i()->click ) )
|
||||
{
|
||||
$this->_doClick();
|
||||
}
|
||||
elseif( isset( Request::i()->follow ) )
|
||||
{
|
||||
$this->_doFollowCommunity();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't feed any content if disabled */
|
||||
if( !Settings::i()->hive_enabled )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Community Hive not enabled' ], 404 );
|
||||
}
|
||||
|
||||
$this->_verifyJwt();
|
||||
}
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
try
|
||||
{
|
||||
match ( $this->payload['request_type'] )
|
||||
{
|
||||
'content' => $this->_getContent(),
|
||||
'sync' => $this->_syncMembers(),
|
||||
'unfollow' => $this->_unfollow()
|
||||
};
|
||||
}
|
||||
catch( \UnhandledMatchError $e )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid request type' ), 400 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Hive click and redirect
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _doClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
$string = base64_decode( Request::i()->click );
|
||||
$result = [];
|
||||
parse_str( $string, $result );
|
||||
|
||||
if( empty( $result['key2' ] ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 missing' );
|
||||
}
|
||||
|
||||
$item = explode( '/', $result['key2'] );
|
||||
|
||||
if( \count( $item ) !== 2 )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 format unexpected' );
|
||||
}
|
||||
|
||||
if( !class_exists( $item[0], TRUE ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 data unexpected' );
|
||||
}
|
||||
|
||||
$object = $item[0]::load( (int) $item[1] );
|
||||
|
||||
if( !( $object instanceof \IPS\Content ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 data unexpected 2' );
|
||||
}
|
||||
|
||||
Output::i()->redirect( $object->url() );
|
||||
}
|
||||
catch( \UnexpectedValueException | \OutOfRangeException $e )
|
||||
{
|
||||
Output::i()->redirect( Url::internal( '' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to Hive follow page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _doFollowCommunity()
|
||||
{
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=hive&controller=account&do=follow', 'front', 'hive_follow' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content for Hive
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _getContent()
|
||||
{
|
||||
/* Make sure we have a valid payload */
|
||||
if( ! isset ( $this->payload['group_hash'] ) )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Missing Group Hash' ], 400 );
|
||||
}
|
||||
|
||||
/* Use new member object for permissions */
|
||||
$member = new Member;
|
||||
|
||||
/* Set secondary groups */
|
||||
$member->member_group_id = Settings::i()->guest_group;
|
||||
if( $this->payload['group_hash'] !== 'guest' )
|
||||
{
|
||||
$member->mgroup_others = $this->payload['group_hash'];
|
||||
}
|
||||
|
||||
$stream = Stream::allActivityStream();
|
||||
$stream->date_relative_days = 365;
|
||||
$stream->id = 0;
|
||||
$stream->include_comments = TRUE;
|
||||
$stream->baseUrl = Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
|
||||
|
||||
/* Content Limitations */
|
||||
$settings = json_decode( Settings::i()->hive_content, TRUE );
|
||||
if( $settings['content_classes'] !== '*' )
|
||||
{
|
||||
$stream->classes = $settings['content_classes'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$stream->classes = '';
|
||||
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
|
||||
{
|
||||
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) and isset( $class::$databaseColumnMap['date'] ) )
|
||||
{
|
||||
$stream->classes .= ',' . $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Container - Stream wants JSON and we have an array... */
|
||||
$stream->containers = json_encode( $settings['content_containers'] );
|
||||
|
||||
$query = $stream->query( $member )->setLimit( 10 );
|
||||
$results = $query->search();
|
||||
|
||||
$return = [];
|
||||
foreach( $results as $comment )
|
||||
{
|
||||
$commentData = $comment->asArray();
|
||||
$commentClass = $commentData['indexData']['index_class'];
|
||||
$itemClass = $commentClass::$itemClass ?? $commentClass;
|
||||
$item = $itemClass::load( $commentData['indexData']['index_item_id'] );
|
||||
|
||||
/* Attachments */
|
||||
$attachment = $fileObj = NULL;
|
||||
try
|
||||
{
|
||||
if( !empty( $commentData['itemData']['attachedImages'] ) )
|
||||
{
|
||||
$fileObj = File::get( $commentData['itemData']['attachedImages'][0]['extension'], $commentData['itemData']['attachedImages'][0]['thumb_location'] ?: $commentData['itemData']['attachedImages'][0]['location'] );
|
||||
}
|
||||
elseif( is_subclass_of( $commentData['indexData']['index_class'], 'IPS\Content\Item' ) AND $contentImage = $item->contentImages( 1, TRUE ) )
|
||||
{
|
||||
$attachType = key( $contentImage[0] );
|
||||
$fileObj = File::get( $attachType, $contentImage[0][ $attachType ] );
|
||||
}
|
||||
|
||||
if( $fileObj !== NULL )
|
||||
{
|
||||
if ( $fileObj->isImage() )
|
||||
{
|
||||
$fileContents = $fileObj->contents();
|
||||
if ( \strlen( $fileContents ) > 1000000 )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
$attachment = [
|
||||
'name' => $fileObj->originalFilename,
|
||||
'file' => base64_encode( $fileContents )
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception | \UnderflowException | \OutOfRangeException $e ) { }
|
||||
|
||||
/* Truncate content */
|
||||
if( mb_strlen( $commentData['indexData']['index_content'] ) > 500 )
|
||||
{
|
||||
$commentData['indexData']['index_content'] = trim( mb_substr( $commentData['indexData']['index_content'], 0, 500 ) ) . '...';
|
||||
}
|
||||
$classObj = $commentClass::load( $commentData['indexData']['index_object_id'] );
|
||||
$return[] = array(
|
||||
'title' => $item->mapped('title'),
|
||||
'content' => $commentData['indexData']['index_content'],
|
||||
'date' => $commentData['indexData']['index_date_commented'],
|
||||
'author' => $classObj->isAnonymous() ? \IPS\Member::loggedIn()->language()->get( "post_anonymously_placename" ) : $commentData['authorData']['name'],
|
||||
'key1' => $itemClass . '/' . $commentData['indexData']['index_item_id'],
|
||||
'key2' => $commentClass . '/' . $commentData['indexData']['index_object_id'],
|
||||
'replies' => ( $item instanceof Item ) ? $item->commentCount() : NULL,
|
||||
'reactions' => IPS::classUsesTrait( $itemClass, 'IPS\Content\Reactable' ) ? $item->reactionCount() : NULL,
|
||||
'image' => $attachment
|
||||
);
|
||||
}
|
||||
|
||||
Output::i()->json([
|
||||
'results' => $return
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync Hive Membership data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _syncMembers()
|
||||
{
|
||||
if( !isset( $this->payload['sync_data'] ) OR !\count( $this->payload['sync_data'] ) )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Missing sync data' ], 400 );
|
||||
}
|
||||
|
||||
$memberIds = array_keys( $this->payload['sync_data'] );
|
||||
$memberData = new ActiveRecordIterator( Db::i()->select( '*', 'core_members', [ Db::i()->in( 'member_id', $memberIds ) ] ), 'IPS\Member' );
|
||||
$respond = [];
|
||||
$seen = [];
|
||||
|
||||
foreach( $memberData as $member )
|
||||
{
|
||||
$generatedHash = Hive::groupHash( $member );
|
||||
|
||||
if( $generatedHash !== $this->payload['sync_data'][ $member->member_id ] )
|
||||
{
|
||||
$respond[ $member->member_id ] = $generatedHash;
|
||||
}
|
||||
$seen[] = $member->member_id;
|
||||
}
|
||||
|
||||
/* Record subscribe confirm */
|
||||
Db::i()->update( 'core_hive_subscribers', [ 'subscribe_confirmed' => 1 ], [ [ 'subscribe_confirmed=?', 0 ], [ Db::i()->in( 'member_id', $memberIds ) ] ] );
|
||||
|
||||
/* Those that haven't been seen, thus deleted */
|
||||
$deleted = array_diff( $memberIds, $seen );
|
||||
|
||||
foreach( $deleted as $del )
|
||||
{
|
||||
$respond[ $del ] = 'guest';
|
||||
}
|
||||
|
||||
Output::i()->json( $respond );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfollow
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _unfollow()
|
||||
{
|
||||
Db::i()->delete( 'core_hive_subscribers', [ 'member_id=?', (int) $this->payload['member_id'] ] );
|
||||
Output::i()->json( 'ok' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify JWT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _verifyJwt()
|
||||
{
|
||||
/* Make sure the request is PUT */
|
||||
if( ! isset( $_SERVER['REQUEST_METHOD'] ) OR mb_strtoupper( $_SERVER['REQUEST_METHOD'] ) !== 'POST' )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid HTTP Method' ), 400 );
|
||||
}
|
||||
|
||||
/* Make sure we have the hive key (activated) */
|
||||
if( ! Settings::i()->hive_enabled )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Community Hive not enabled' ), 404 );
|
||||
}
|
||||
|
||||
/* Do we have the JWT */
|
||||
$token = file_get_contents('php://input');
|
||||
if( empty( $token ) )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Missing JWT' ), 401 );
|
||||
}
|
||||
|
||||
$jwk = new \Jose\Component\Core\JWK([
|
||||
'kty' => 'oct',
|
||||
'k' => base64_encode( Settings::i()->hive_key )
|
||||
]);
|
||||
|
||||
$jwsVerifier = new \Jose\Component\Signature\JWSVerifier(
|
||||
new \Jose\Component\Core\AlgorithmManager([ new \Jose\Component\Signature\Algorithm\HS256() ])
|
||||
);
|
||||
|
||||
$jwsCompactSerializer = new CompactSerializer();
|
||||
|
||||
$data = $jwsCompactSerializer->unserialize( $token );
|
||||
if( !$jwsVerifier->verifyWithKey( $data, $jwk, 0 ) )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid JWT' ), 401 );
|
||||
}
|
||||
|
||||
$this->payload = json_decode( $data->getPayload(), true );
|
||||
|
||||
$claimCheckerManager = new ClaimCheckerManager(
|
||||
[
|
||||
new IssuerChecker( ['communityhive'] ),
|
||||
new AudienceChecker( rtrim( Settings::i()->base_url, '/' ) ),
|
||||
new IssuedAtChecker( 1000 ),
|
||||
new NotBeforeChecker( 1000 ),
|
||||
new ExpirationTimeChecker( 1000 )
|
||||
]
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
$claimCheckerManager->check( $this->payload, [ 'iss', 'sub', 'exp', 'aud', 'nbf', 'iat' ] );
|
||||
}
|
||||
catch( MissingMandatoryClaimException | InvalidClaimException $e )
|
||||
{
|
||||
Output::i()->json( array( 'message' => $e->getMessage() ), 400 );
|
||||
}
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
}
|
||||
@@ -322,8 +322,8 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$clubsCount = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), NULL, TRUE );
|
||||
$allClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member );
|
||||
$clubsCount = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), \IPS\Member::loggedIn()->modPermission( 'can_access_all_clubs' ) ? NULL : "( show_membertab = 'nonmember' OR show_membertab IS NULL )", TRUE );
|
||||
$allClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), \IPS\Member::loggedIn()->modPermission( 'can_access_all_clubs' ) ? NULL : "( show_membertab = 'nonmember' OR show_membertab IS NULL )" );
|
||||
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, ( ceil( $clubsCount / $perPage ) ), $page, $perPage );
|
||||
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/clubs.css', 'core', 'front' ) );
|
||||
@@ -609,9 +609,9 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
$where[] = $currentClass::commentWhere();
|
||||
}
|
||||
|
||||
|
||||
$baseUrl = \IPS\Http\Url::internal( "app=core&module=members&controller=profile&id={$this->member->member_id}&do=content", 'front', 'profile_content', $this->member->members_seo_name );
|
||||
$output = new \IPS\Helpers\Table\Content( $currentClass, $baseUrl->setQueryString( array( 'type' => $currentType ) ), $where, NULL, NULL, 'read', FALSE );
|
||||
$output = new \IPS\Helpers\Table\Content( $currentClass, $baseUrl->setQueryString( array( 'type' => $currentType ) ), $where, NULL, \IPS\Content\Hideable::FILTER_AUTOMATIC, 'read', FALSE );
|
||||
}
|
||||
|
||||
if ( $currentType == 'core_statuses_status' )
|
||||
|
||||
@@ -535,7 +535,7 @@ class _messenger extends \IPS\Content\Controller
|
||||
} );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'move_message_to', NULL, FALSE, array( 'options' => array_merge( array( 'myconvo' => \IPS\Member::loggedIn()->language()->addToStack('inbox') ), $folders ), 'parse' => 'normal' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'move_message_to', NULL, FALSE, array( 'options' => array( 'myconvo' => \IPS\Member::loggedIn()->language()->addToStack('inbox') ) + $folders, 'parse' => 'normal' ) ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
|
||||
@@ -316,12 +316,12 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
case 'IPS\Helpers\Form\Email':
|
||||
case 'IPS\Helpers\Form\TextArea':
|
||||
case 'IPS\Helpers\Form\Url':
|
||||
case 'IPS\Helpers\Form\Date':
|
||||
case 'IPS\Helpers\Form\Number':
|
||||
$fieldName = 'core_pfield_' . $id;
|
||||
|
||||
|
||||
if( isset( \IPS\Request::i()->$fieldName ) )
|
||||
{
|
||||
|
||||
if( \IPS\Data\Store::i()->profileFields['fields'][$group][$id]['pf_search_type'] == 'loose' )
|
||||
{
|
||||
$where[] = array( 'LOWER(core_pfields_content.field_' . $id . ') LIKE ?', '%' . mb_strtolower( \IPS\Request::i()->$fieldName ) . '%' );
|
||||
@@ -333,6 +333,21 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
$baseUrl = $baseUrl->setQueryString( $fieldName, \IPS\Request::i()->$fieldName );
|
||||
}
|
||||
break;
|
||||
case 'IPS\Helpers\Form\Date':
|
||||
$fieldName = 'core_pfield_' . $id;
|
||||
if ( is_array( \IPS\Request::i()->$fieldName ) AND \IPS\Request::i()->$fieldName['start'] )
|
||||
{
|
||||
$start = new \IPS\DateTime( \IPS\Request::i()->$fieldName['start'] );
|
||||
|
||||
$where[] = "core_pfields_content.field_{$id}>" . $start->getTimestamp();
|
||||
}
|
||||
if ( is_array( \IPS\Request::i()->$fieldName ) AND \IPS\Request::i()->$fieldName['end'] )
|
||||
{
|
||||
$end = new \IPS\DateTime( \IPS\Request::i()->$fieldName['end'] );
|
||||
|
||||
$where[] = "core_pfields_content.field_{$id}<" . $end->getTimestamp();
|
||||
}
|
||||
break;
|
||||
case 'IPS\Helpers\Form\Select':
|
||||
case 'IPS\Helpers\Form\CheckboxSet':
|
||||
case 'IPS\Helpers\Form\Radio':
|
||||
@@ -498,27 +513,27 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( isset( \IPS\Request::i()->search_min_comments ) )
|
||||
{
|
||||
$filter->minimumComments( \IPS\Request::i()->search_min_comments );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_comments', \IPS\Request::i()->search_min_comments );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_comments', FALSE, array( 'sprintf' => array( \IPS\Request::i()->search_min_comments ) ) );
|
||||
$filter->minimumComments( (int) \IPS\Request::i()->search_min_comments );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_comments', (int) \IPS\Request::i()->search_min_comments );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_comments', FALSE, [ 'sprintf' => [ (int) \IPS\Request::i()->search_min_comments ] ] );
|
||||
}
|
||||
if ( isset( \IPS\Request::i()->search_min_replies ) AND isset( $class::$commentClass ) )
|
||||
{
|
||||
$filter->minimumComments( \IPS\Request::i()->search_min_replies + 1 );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_replies', \IPS\Request::i()->search_min_replies );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_replies', FALSE, array( 'sprintf' => array( \IPS\Request::i()->search_min_replies ) ) );
|
||||
$filter->minimumComments( (int) \IPS\Request::i()->search_min_replies + 1 );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_replies', (int) \IPS\Request::i()->search_min_replies );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_replies', FALSE, [ 'sprintf' => [ (int) \IPS\Request::i()->search_min_replies ] ] );
|
||||
}
|
||||
if ( isset( \IPS\Request::i()->search_min_reviews ) AND isset( $class::$reviewClass ) )
|
||||
{
|
||||
$filter->minimumReviews( \IPS\Request::i()->search_min_reviews );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_reviews', \IPS\Request::i()->search_min_reviews );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_reviews', FALSE, array( 'sprintf' => array( \IPS\Request::i()->search_min_reviews ) ) );
|
||||
$filter->minimumReviews( (int) \IPS\Request::i()->search_min_reviews );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_reviews', (int) \IPS\Request::i()->search_min_reviews );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_reviews', FALSE, [ 'sprintf' => [ (int) \IPS\Request::i()->search_min_reviews ] ] );
|
||||
}
|
||||
if ( isset( \IPS\Request::i()->search_min_views ) AND isset( $class::$databaseColumnMap['views'] ) AND \IPS\Content\Search\Index::i()->supportViewFiltering() )
|
||||
{
|
||||
$filter->minimumViews( \IPS\Request::i()->search_min_views );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_views', \IPS\Request::i()->search_min_views );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_views', FALSE, array( 'sprintf' => array( \IPS\Request::i()->search_min_views ) ) );
|
||||
$filter->minimumViews( (int) \IPS\Request::i()->search_min_views );
|
||||
$baseUrl = $baseUrl->setQueryString( 'search_min_views', (int) \IPS\Request::i()->search_min_views );
|
||||
$titleConditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'search_blurb_min_views', FALSE, [ 'sprintf' => [ (int) \IPS\Request::i()->search_min_views ] ] );
|
||||
}
|
||||
|
||||
$query->filterByContent( array( $filter ) );
|
||||
|
||||
@@ -314,7 +314,7 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
public function topContributors()
|
||||
{
|
||||
/* How many? */
|
||||
$limit = \intval( ( isset( \IPS\Request::i()->limit ) and \IPS\Request::i()->limit <= 25 ) ? \IPS\Request::i()->limit : 5 );
|
||||
$limit = \intval( ( isset( \IPS\Request::i()->limit ) and \IPS\Request::i()->limit <= 25 and \IPS\Request::i()->limit > 0 ) ? \IPS\Request::i()->limit : 5 );
|
||||
|
||||
/* What timeframe? */
|
||||
$where = array( array( 'member_received > 0' ) );
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Helpers\Form\YesNo;
|
||||
use IPS\Output;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -18,12 +22,19 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie Policy
|
||||
* Cookie Information Page
|
||||
*/
|
||||
class _cookies extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
public function execute()
|
||||
{
|
||||
parent::execute();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Privacy Policy
|
||||
* Cookie Information Page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -35,7 +46,35 @@ class _cookies extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('cookies_about') );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cookies_about');
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->metaTags['robots'] = 'noindex';
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->cookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opt out of optional cookies
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function cookieConsentToggle()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
\IPS\Member::loggedIn()->setAllowOptionalCookies( (bool) \IPS\Request::i()->status );
|
||||
if ( isset( \IPS\Request::i()->ref ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$url = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
|
||||
}
|
||||
catch( \IPS\Http\Url\Exception $e )
|
||||
{
|
||||
$url = NULL;
|
||||
}
|
||||
|
||||
if ( $url instanceof \IPS\Http\Url\Internal and !$url->openRedirect() )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $url );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,7 +394,7 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
throw new \DomainException( 'q_and_a_incorrect' );
|
||||
}
|
||||
|
||||
$answers = json_decode( $question['qa_answers'] );
|
||||
$answers = json_decode( $question['qa_answers'], true );
|
||||
|
||||
if( $answers )
|
||||
{
|
||||
@@ -551,9 +551,11 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
/* Query spam service */
|
||||
$spamCode = NULL;
|
||||
$spamAction = NULL;
|
||||
$disposable = FALSE;
|
||||
$geoBlock = FALSE;
|
||||
if( \IPS\Settings::i()->spam_service_enabled )
|
||||
{
|
||||
$spamAction = $member->spamService( 'register', NULL, $spamCode );
|
||||
$spamAction = $member->spamService( 'register', NULL, $spamCode, $disposable, $geoBlock );
|
||||
if( $spamAction == 4 )
|
||||
{
|
||||
\IPS\Output::i()->error( 'spam_denied_account', '2S129/1', 403, '' );
|
||||
@@ -575,7 +577,7 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Save anything the profile extensions did */
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'account', array( 'type' => 'register', 'spamCode' => $spamCode, 'spamAction' => $spamAction ), FALSE );
|
||||
$member->logHistory( 'core', 'account', array( 'type' => 'register', 'spamCode' => $spamCode, 'spamAction' => $spamAction, 'disposable' => $disposable, 'geoBlock' => $geoBlock ), FALSE );
|
||||
|
||||
/* Security Questions */
|
||||
if ( \IPS\Settings::i()->security_questions_enabled and \in_array( \IPS\Settings::i()->security_questions_prompt, array( 'register', 'optional' ) ) )
|
||||
@@ -834,6 +836,11 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
elseif ( \IPS\Member::loggedIn()->real_name and \IPS\Member::loggedIn()->email )
|
||||
{
|
||||
/* If we somehow came here from the oauth authorization prompt but the member, redirect back there */
|
||||
if ( isset( \IPS\Request::i()->oauth ) and $ref = static::_refUrl() and $ref->base === 'none' )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $ref );
|
||||
}
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,6 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Show our own oauth clients? */
|
||||
$showApps = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( array( 'oauth_enabled=1 AND oauth_ucp=1' ) ) )->first();
|
||||
|
||||
/* Return */
|
||||
return \IPS\Theme::i()->getTemplate( 'system' )->settings( $area, $output, ( \IPS\Settings::i()->allow_email_changes != 'disabled' ), $canChangePassword, \IPS\Member::loggedIn()->group['g_dname_changes'], $canChangeSignature, $loginMethods, $canConfigureMfa, $showApps );
|
||||
}
|
||||
@@ -240,8 +239,14 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
/* Build the form */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->class = 'ipsForm_collapseTablet';
|
||||
$form->addDummy( 'current_email', htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'new_email', '', TRUE, array( 'accountEmail' => \IPS\Member::loggedIn() ) ) );
|
||||
$currentEmail = htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
$form->addDummy( 'current_email', \IPS\Member::loggedIn()->members_bitoptions["email_messages_bounce"] ? \IPS\Theme::i()->getTemplate( 'global', 'core' )->memberEmailBlockedMessage( $currentEmail ) : $currentEmail );
|
||||
$form->add( new \IPS\Helpers\Form\Email(
|
||||
'new_email',
|
||||
'',
|
||||
TRUE,
|
||||
array( 'accountEmail' => \IPS\Member::loggedIn() )
|
||||
) );
|
||||
|
||||
/* Handle submissions */
|
||||
$values = NULL;
|
||||
@@ -634,7 +639,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
return \IPS\Theme::i()->getTemplate( 'system' )->settingsMfaPassword( $login, $error );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Get our handlers and the output, even if it's just for a backdrop */
|
||||
$handlers = array();
|
||||
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
|
||||
@@ -683,6 +688,9 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Member::loggedIn()->members_bitoptions['security_questions_opt_out'] = FALSE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
|
||||
/* Invalidate other sessions */
|
||||
\IPS\Member::loggedIn()->invalidateSessionsAndLogins( \IPS\Session::i()->id );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
|
||||
}
|
||||
|
||||
@@ -720,10 +728,128 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$output.= \IPS\Theme::i()->getTemplate( 'system' )->settingsPrivacy();
|
||||
|
||||
/* If we're still here, just show the screen */
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request personal identifiable information
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function requestPiiData()
|
||||
{
|
||||
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member\PrivacyAction::canRequestPiiData() OR \IPS\Settings::i()->pii_type !== 'on' )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '1C122/10', 403, '' );
|
||||
}
|
||||
\IPS\Member\PrivacyAction::requestPiiData();
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')->setFragment('piiDataRequest'), 'pii_requested' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Download personal identifiable information
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function downloadPiiData()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member\PrivacyAction::canDownloadPiiData() OR \IPS\Settings::i()->pii_type !== 'on' )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '1C122/11', 403, '' );
|
||||
}
|
||||
|
||||
$xml = \IPS\Member::loggedIn()->getPiiData();
|
||||
|
||||
\IPS\Db::i()->delete( 'core_member_privacy_actions', array( 'member_id=? AND action=?', \IPS\Member::loggedIn()->member_id, \IPS\Member\PrivacyAction::TYPE_REQUEST_PII ) );
|
||||
\IPS\Db::i()->delete( 'core_notifications', array( 'member=? AND notification_key=?', \IPS\Member::loggedIn()->member_id, 'pii_data' ) );
|
||||
\IPS\Member::loggedIn()->logHistory( 'core', 'privacy', array( 'type' => 'pii_download' ) );
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', [ 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', \IPS\Member::loggedIn()->name.'_personal_information.xml' ) ], FALSE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Request account deletion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function requestAccountDeletion()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if( !isset( $_SESSION['passwordValidatedForMfa'] ) OR !\IPS\Member::loggedIn()->canUseAccountDeletion() OR \IPS\Settings::i()->right_to_be_forgotten_type !== 'on' )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C122/13', 403, '' );
|
||||
}
|
||||
|
||||
if( !\IPS\Member\PrivacyAction::canDeleteAccount() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '1C122/12', 403, '' );
|
||||
}
|
||||
if( \IPS\Request::i()->vkey )
|
||||
{
|
||||
\IPS\Member\PrivacyAction::requestAccountDeletion( NULL, FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Member\PrivacyAction::requestAccountDeletion(NULL, TRUE );
|
||||
}
|
||||
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front')->setFragment('requestAccountDeletion'), 'account_deletion_requested' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel account deletion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function cancelAccountDeletion()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
try
|
||||
{
|
||||
$where = [];
|
||||
$where[] = ['member_id=?', \IPS\Member::loggedIn()->member_id];
|
||||
$where[] = [ \IPS\Db::i()->in( 'action',[\IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE, \IPS\Member\PrivacyAction::TYPE_REQUEST_DELETE_VALIDATION ] ) ];
|
||||
$row = \IPS\Db::i()->select( '*', \IPS\Member\PrivacyAction::$databaseTable, $where )->first();
|
||||
\IPS\Member\PrivacyAction::constructFromData( $row )->delete();
|
||||
\IPS\Member::loggedIn()->logHistory( 'core', 'privacy', [ 'type' => 'account_deletion_cancelled' ] );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front'), 'account_deletion_cancelled' );
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C122/Y', 404, '' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm account deletion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function confirmAccountDeletion()
|
||||
{
|
||||
$key = \IPS\Request::i()->vid;
|
||||
try
|
||||
{
|
||||
$request = \IPS\Member\PrivacyAction::getDeletionRequestByMemberAndKey( \IPS\Member::loggedIn(), $key );
|
||||
|
||||
$request->confirmAccountDeletion();
|
||||
\IPS\Output::i()->redirect( $this->url->setQueryString( 'area','mfa'), 'account_deletion_confirmed' );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C122/Z', 404, '' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial MFA Setup
|
||||
@@ -1225,8 +1351,9 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
$content = nl2br( trim( $pre->nodeValue ) );
|
||||
$preBreaks += \count( explode( "<br />", $content ) );
|
||||
}
|
||||
|
||||
if ( \is_numeric( $sigLimits[5] ) and ( $signature->getElementsByTagName('p')->length + $signature->getElementsByTagName('br')->length + $preBreaks ) > $sigLimits[5] )
|
||||
|
||||
/* Line limit with a sensible length restriction to prevent broken html */
|
||||
if ( ( \is_numeric( $sigLimits[5] ) and ( $signature->getElementsByTagName('p')->length + $signature->getElementsByTagName('br')->length + $preBreaks ) > $sigLimits[5] ) or \strlen( $values['signature'] ) > 20000 )
|
||||
{
|
||||
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_lines_exceeded');
|
||||
}
|
||||
@@ -1729,6 +1856,12 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Check validation */
|
||||
if( !isset( $_SESSION['passwordValidatedForMfa'] ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '1C122/14', 403, '' );
|
||||
}
|
||||
|
||||
/* Check this value can be toggled */
|
||||
if( \IPS\Member::loggedIn()->group['g_hide_online_list'] >= 1 )
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user